LogNormalDistribution.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.continuous;

  22. import org.hipparchus.exception.LocalizedCoreFormats;
  23. import org.hipparchus.exception.MathIllegalArgumentException;
  24. import org.hipparchus.special.Erf;
  25. import org.hipparchus.util.FastMath;

  26. /**
  27.  * Implementation of the log-normal (gaussian) distribution.
  28.  * <p>
  29.  * <strong>Parameters:</strong>
  30.  * {@code X} is log-normally distributed if its natural logarithm {@code log(X)}
  31.  * is normally distributed. The probability distribution function of {@code X}
  32.  * is given by (for {@code x > 0})
  33.  * <p>
  34.  * {@code exp(-0.5 * ((ln(x) - m) / s)^2) / (s * sqrt(2 * pi) * x)}
  35.  * <ul>
  36.  * <li>{@code m} is the <em>location</em> parameter: this is the mean of the
  37.  * normally distributed natural logarithm of this distribution,</li>
  38.  * <li>{@code s} is the <em>shape</em> parameter: this is the standard
  39.  * deviation of the normally distributed natural logarithm of this
  40.  * distribution.
  41.  * </ul>
  42.  *
  43.  * @see <a href="http://en.wikipedia.org/wiki/Log-normal_distribution">
  44.  * Log-normal distribution (Wikipedia)</a>
  45.  * @see <a href="http://mathworld.wolfram.com/LogNormalDistribution.html">
  46.  * Log Normal distribution (MathWorld)</a>
  47.  */
  48. public class LogNormalDistribution extends AbstractRealDistribution {

  49.     /** Serializable version identifier. */
  50.     private static final long serialVersionUID = 20120112;

  51.     /** &radic;(2 &pi;) */
  52.     private static final double SQRT2PI = FastMath.sqrt(2 * FastMath.PI);

  53.     /** &radic;(2) */
  54.     private static final double SQRT2 = FastMath.sqrt(2.0);

  55.     /** The location parameter of this distribution (named m in MathWorld and ยต in Wikipedia). */
  56.     private final double location;

  57.     /** The shape parameter of this distribution. */
  58.     private final double shape;
  59.     /** The value of {@code log(shape) + 0.5 * log(2*PI)} stored for faster computation. */
  60.     private final double logShapePlusHalfLog2Pi;

  61.     /**
  62.      * Create a log-normal distribution, where the mean and standard deviation
  63.      * of the {@link NormalDistribution normally distributed} natural
  64.      * logarithm of the log-normal distribution are equal to zero and one
  65.      * respectively. In other words, the location of the returned distribution is
  66.      * {@code 0}, while its shape is {@code 1}.
  67.      */
  68.     public LogNormalDistribution() {
  69.         this(0, 1);
  70.     }

  71.     /**
  72.      * Create a log-normal distribution using the specified location and shape.
  73.      *
  74.      * @param location the location parameter of this distribution
  75.      * @param shape the shape parameter of this distribution
  76.      * @throws MathIllegalArgumentException if {@code shape <= 0}.
  77.      */
  78.     public LogNormalDistribution(double location, double shape)
  79.         throws MathIllegalArgumentException {
  80.         this(location, shape, DEFAULT_SOLVER_ABSOLUTE_ACCURACY);
  81.     }


  82.     /**
  83.      * Creates a log-normal distribution.
  84.      *
  85.      * @param location Location parameter of this distribution.
  86.      * @param shape Shape parameter of this distribution.
  87.      * @param inverseCumAccuracy Inverse cumulative probability accuracy.
  88.      * @throws MathIllegalArgumentException if {@code shape <= 0}.
  89.      */
  90.     public LogNormalDistribution(double location,
  91.                                  double shape,
  92.                                  double inverseCumAccuracy)
  93.         throws MathIllegalArgumentException {
  94.         super(inverseCumAccuracy);

  95.         if (shape <= 0) {
  96.             throw new MathIllegalArgumentException(LocalizedCoreFormats.SHAPE, shape);
  97.         }

  98.         this.location = location;
  99.         this.shape = shape;
  100.         this.logShapePlusHalfLog2Pi = FastMath.log(shape) + 0.5 * FastMath.log(2 * FastMath.PI);
  101.     }

  102.     /**
  103.      * Returns the location parameter of this distribution.
  104.      *
  105.      * @return the location parameter
  106.      * @since 1.4
  107.      */
  108.     public double getLocation() {
  109.         return location;
  110.     }

  111.     /**
  112.      * Returns the shape parameter of this distribution.
  113.      *
  114.      * @return the shape parameter
  115.      */
  116.     public double getShape() {
  117.         return shape;
  118.     }

  119.     /**
  120.      * {@inheritDoc}
  121.      *
  122.      * For location {@code m}, and shape {@code s} of this distribution, the PDF
  123.      * is given by
  124.      * <ul>
  125.      * <li>{@code 0} if {@code x <= 0},</li>
  126.      * <li>{@code exp(-0.5 * ((ln(x) - m) / s)^2) / (s * sqrt(2 * pi) * x)}
  127.      * otherwise.</li>
  128.      * </ul>
  129.      */
  130.     @Override
  131.     public double density(double x) {
  132.         if (x <= 0) {
  133.             return 0;
  134.         }
  135.         final double x0 = FastMath.log(x) - location;
  136.         final double x1 = x0 / shape;
  137.         return FastMath.exp(-0.5 * x1 * x1) / (shape * SQRT2PI * x);
  138.     }

  139.     /** {@inheritDoc}
  140.      *
  141.      * See documentation of {@link #density(double)} for computation details.
  142.      */
  143.     @Override
  144.     public double logDensity(double x) {
  145.         if (x <= 0) {
  146.             return Double.NEGATIVE_INFINITY;
  147.         }
  148.         final double logX = FastMath.log(x);
  149.         final double x0 = logX - location;
  150.         final double x1 = x0 / shape;
  151.         return -0.5 * x1 * x1 - (logShapePlusHalfLog2Pi + logX);
  152.     }

  153.     /**
  154.      * {@inheritDoc}
  155.      *
  156.      * For location {@code m}, and shape {@code s} of this distribution, the CDF
  157.      * is given by
  158.      * <ul>
  159.      * <li>{@code 0} if {@code x <= 0},</li>
  160.      * <li>{@code 0} if {@code ln(x) - m < 0} and {@code m - ln(x) > 40 * s}, as
  161.      * in these cases the actual value is within {@code Double.MIN_VALUE} of 0,
  162.      * <li>{@code 1} if {@code ln(x) - m >= 0} and {@code ln(x) - m > 40 * s},
  163.      * as in these cases the actual value is within {@code Double.MIN_VALUE} of 1,</li>
  164.      * <li>{@code 0.5 + 0.5 * erf((ln(x) - m) / (s * sqrt(2))} otherwise.</li>
  165.      * </ul>
  166.      */
  167.     @Override
  168.     public double cumulativeProbability(double x)  {
  169.         if (x <= 0) {
  170.             return 0;
  171.         }
  172.         final double dev = FastMath.log(x) - location;
  173.         if (FastMath.abs(dev) > 40 * shape) {
  174.             return dev < 0 ? 0.0d : 1.0d;
  175.         }
  176.         return 0.5 + 0.5 * Erf.erf(dev / (shape * SQRT2));
  177.     }

  178.     /** {@inheritDoc} */
  179.     @Override
  180.     public double probability(double x0,
  181.                               double x1)
  182.         throws MathIllegalArgumentException {
  183.         if (x0 > x1) {
  184.             throw new MathIllegalArgumentException(LocalizedCoreFormats.LOWER_ENDPOINT_ABOVE_UPPER_ENDPOINT,
  185.                                                 x0, x1, true);
  186.         }
  187.         if (x0 <= 0 || x1 <= 0) {
  188.             return super.probability(x0, x1);
  189.         }
  190.         final double denom = shape * SQRT2;
  191.         final double v0 = (FastMath.log(x0) - location) / denom;
  192.         final double v1 = (FastMath.log(x1) - location) / denom;
  193.         return 0.5 * Erf.erf(v0, v1);
  194.     }

  195.     /**
  196.      * {@inheritDoc}
  197.      *
  198.      * For location {@code m} and shape {@code s}, the mean is
  199.      * {@code exp(m + s^2 / 2)}.
  200.      */
  201.     @Override
  202.     public double getNumericalMean() {
  203.         double s = shape;
  204.         return FastMath.exp(location + (s * s / 2));
  205.     }

  206.     /**
  207.      * {@inheritDoc}
  208.      *
  209.      * For location {@code m} and shape {@code s}, the variance is
  210.      * {@code (exp(s^2) - 1) * exp(2 * m + s^2)}.
  211.      */
  212.     @Override
  213.     public double getNumericalVariance() {
  214.         final double s = shape;
  215.         final double ss = s * s;
  216.         return (FastMath.expm1(ss)) * FastMath.exp(2 * location + ss);
  217.     }

  218.     /**
  219.      * {@inheritDoc}
  220.      *
  221.      * The lower bound of the support is always 0 no matter the parameters.
  222.      *
  223.      * @return lower bound of the support (always 0)
  224.      */
  225.     @Override
  226.     public double getSupportLowerBound() {
  227.         return 0;
  228.     }

  229.     /**
  230.      * {@inheritDoc}
  231.      *
  232.      * The upper bound of the support is always positive infinity
  233.      * no matter the parameters.
  234.      *
  235.      * @return upper bound of the support (always
  236.      * {@code Double.POSITIVE_INFINITY})
  237.      */
  238.     @Override
  239.     public double getSupportUpperBound() {
  240.         return Double.POSITIVE_INFINITY;
  241.     }

  242.     /**
  243.      * {@inheritDoc}
  244.      *
  245.      * The support of this distribution is connected.
  246.      *
  247.      * @return {@code true}
  248.      */
  249.     @Override
  250.     public boolean isSupportConnected() {
  251.         return true;
  252.     }
  253. }