LaplaceDistribution.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.  *      http://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. package org.apache.commons.statistics.distribution;

  18. /**
  19.  * Implementation of the Laplace distribution.
  20.  *
  21.  * <p>The probability density function of \( X \) is:
  22.  *
  23.  * <p>\[ f(x; \mu, b) = \frac{1}{2b} \exp \left( -\frac{|x-\mu|}{b} \right) \]
  24.  *
  25.  * <p>for \( \mu \) the location,
  26.  * \( b &gt; 0 \) the scale, and
  27.  * \( x \in (-\infty, \infty) \).
  28.  *
  29.  * @see <a href="https://en.wikipedia.org/wiki/Laplace_distribution">Laplace distribution (Wikipedia)</a>
  30.  * @see <a href="https://mathworld.wolfram.com/LaplaceDistribution.html">Laplace distribution (MathWorld)</a>
  31.  */
  32. public final class LaplaceDistribution extends AbstractContinuousDistribution {
  33.     /** The location parameter. */
  34.     private final double mu;
  35.     /** The scale parameter. */
  36.     private final double beta;
  37.     /** log(2 * beta). */
  38.     private final double log2beta;

  39.     /**
  40.      * @param mu Location parameter.
  41.      * @param beta Scale parameter (must be positive).
  42.      */
  43.     private LaplaceDistribution(double mu,
  44.                                 double beta) {
  45.         this.mu = mu;
  46.         this.beta = beta;
  47.         log2beta = Math.log(2.0 * beta);
  48.     }

  49.     /**
  50.      * Creates a Laplace distribution.
  51.      *
  52.      * @param mu Location parameter.
  53.      * @param beta Scale parameter (must be positive).
  54.      * @return the distribution
  55.      * @throws IllegalArgumentException if {@code beta <= 0}
  56.      */
  57.     public static LaplaceDistribution of(double mu,
  58.                                          double beta) {
  59.         if (beta <= 0) {
  60.             throw new DistributionException(DistributionException.NOT_STRICTLY_POSITIVE, beta);
  61.         }
  62.         return new LaplaceDistribution(mu, beta);
  63.     }

  64.     /**
  65.      * Gets the location parameter of this distribution.
  66.      *
  67.      * @return the location parameter.
  68.      */
  69.     public double getLocation() {
  70.         return mu;
  71.     }

  72.     /**
  73.      * Gets the scale parameter of this distribution.
  74.      *
  75.      * @return the scale parameter.
  76.      */
  77.     public double getScale() {
  78.         return beta;
  79.     }

  80.     /** {@inheritDoc} */
  81.     @Override
  82.     public double density(double x) {
  83.         return Math.exp(-Math.abs(x - mu) / beta) / (2.0 * beta);
  84.     }

  85.     /** {@inheritDoc} */
  86.     @Override
  87.     public double logDensity(double x) {
  88.         return -Math.abs(x - mu) / beta - log2beta;
  89.     }

  90.     /** {@inheritDoc} */
  91.     @Override
  92.     public double cumulativeProbability(double x) {
  93.         if (x <= mu) {
  94.             return 0.5 * Math.exp((x - mu) / beta);
  95.         }
  96.         return 1.0 - 0.5 * Math.exp((mu - x) / beta);
  97.     }

  98.     /** {@inheritDoc} */
  99.     @Override
  100.     public double survivalProbability(double x) {
  101.         if (x <= mu) {
  102.             return 1.0 - 0.5 * Math.exp((x - mu) / beta);
  103.         }
  104.         return 0.5 * Math.exp((mu - x) / beta);
  105.     }

  106.     /** {@inheritDoc} */
  107.     @Override
  108.     public double inverseCumulativeProbability(double p) {
  109.         ArgumentUtils.checkProbability(p);
  110.         if (p == 0) {
  111.             return Double.NEGATIVE_INFINITY;
  112.         } else if (p == 1) {
  113.             return Double.POSITIVE_INFINITY;
  114.         }
  115.         final double x = (p > 0.5) ? -Math.log(2.0 * (1.0 - p)) : Math.log(2.0 * p);
  116.         return mu + beta * x;
  117.     }

  118.     /** {@inheritDoc} */
  119.     @Override
  120.     public double inverseSurvivalProbability(double p) {
  121.         ArgumentUtils.checkProbability(p);
  122.         if (p == 1) {
  123.             return Double.NEGATIVE_INFINITY;
  124.         } else if (p == 0) {
  125.             return Double.POSITIVE_INFINITY;
  126.         }
  127.         // By symmetry: x = -icdf(p); then transform back by the scale and location
  128.         final double x = (p > 0.5) ? Math.log(2.0 * (1.0 - p)) : -Math.log(2.0 * p);
  129.         return mu + beta * x;
  130.     }

  131.     /**
  132.      * {@inheritDoc}
  133.      *
  134.      * <p>The mean is equal to the {@linkplain #getLocation() location}.
  135.      */
  136.     @Override
  137.     public double getMean() {
  138.         return getLocation();
  139.     }

  140.     /**
  141.      * {@inheritDoc}
  142.      *
  143.      * <p>For scale parameter \( b \), the variance is \( 2 b^2 \).
  144.      */
  145.     @Override
  146.     public double getVariance() {
  147.         return 2.0 * beta * beta;
  148.     }

  149.     /**
  150.      * {@inheritDoc}
  151.      *
  152.      * <p>The lower bound of the support is always negative infinity.
  153.      *
  154.      * @return {@linkplain Double#NEGATIVE_INFINITY negative infinity}.
  155.      */
  156.     @Override
  157.     public double getSupportLowerBound() {
  158.         return Double.NEGATIVE_INFINITY;
  159.     }

  160.     /**
  161.      * {@inheritDoc}
  162.      *
  163.      * <p>The upper bound of the support is always positive infinity.
  164.      *
  165.      * @return {@linkplain Double#POSITIVE_INFINITY positive infinity}.
  166.      */
  167.     @Override
  168.     public double getSupportUpperBound() {
  169.         return Double.POSITIVE_INFINITY;
  170.     }

  171.     /** {@inheritDoc} */
  172.     @Override
  173.     double getMedian() {
  174.         // Overridden for the probability(double, double) method.
  175.         // This is intentionally not a public method.
  176.         return mu;
  177.     }
  178. }