CauchyDistribution.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. import org.apache.commons.rng.UniformRandomProvider;
  19. import org.apache.commons.rng.sampling.distribution.StableSampler;

  20. /**
  21.  * Implementation of the Cauchy distribution.
  22.  *
  23.  * <p>The probability density function of \( X \) is:
  24.  *
  25.  * <p>\[ f(x; x_0, \gamma) = { 1 \over \pi \gamma } \left[ { \gamma^2 \over (x - x_0)^2 + \gamma^2  } \right] \]
  26.  *
  27.  * <p>for \( x_0 \) the location,
  28.  * \( \gamma &gt; 0 \) the scale, and
  29.  * \( x \in (-\infty, \infty) \).
  30.  *
  31.  * @see <a href="https://en.wikipedia.org/wiki/Cauchy_distribution">Cauchy distribution (Wikipedia)</a>
  32.  * @see <a href="https://mathworld.wolfram.com/CauchyDistribution.html">Cauchy distribution (MathWorld)</a>
  33.  */
  34. public final class CauchyDistribution extends AbstractContinuousDistribution {
  35.     /** The location of this distribution. */
  36.     private final double location;
  37.     /** The scale of this distribution. */
  38.     private final double scale;
  39.     /** Density factor (scale / pi). */
  40.     private final double scaleOverPi;
  41.     /** Density factor (scale^2). */
  42.     private final double scale2;

  43.     /**
  44.      * @param location Location parameter.
  45.      * @param scale Scale parameter.
  46.      */
  47.     private CauchyDistribution(double location,
  48.                                double scale) {
  49.         this.scale = scale;
  50.         this.location = location;
  51.         scaleOverPi = scale / Math.PI;
  52.         scale2 = scale * scale;
  53.     }

  54.     /**
  55.      * Creates a Cauchy distribution.
  56.      *
  57.      * @param location Location parameter.
  58.      * @param scale Scale parameter.
  59.      * @return the distribution
  60.      * @throws IllegalArgumentException if {@code scale <= 0}.
  61.      */
  62.     public static CauchyDistribution of(double location,
  63.                                         double scale) {
  64.         if (scale <= 0) {
  65.             throw new DistributionException(DistributionException.NOT_STRICTLY_POSITIVE, scale);
  66.         }
  67.         return new CauchyDistribution(location, scale);
  68.     }

  69.     /**
  70.      * Gets the location parameter of this distribution.
  71.      *
  72.      * @return the location parameter.
  73.      */
  74.     public double getLocation() {
  75.         return location;
  76.     }

  77.     /**
  78.      * Gets the scale parameter of this distribution.
  79.      *
  80.      * @return the scale parameter.
  81.      */
  82.     public double getScale() {
  83.         return scale;
  84.     }

  85.     /** {@inheritDoc} */
  86.     @Override
  87.     public double density(double x) {
  88.         final double dev = x - location;
  89.         return scaleOverPi / (dev * dev + scale2);
  90.     }

  91.     /** {@inheritDoc} */
  92.     @Override
  93.     public double cumulativeProbability(double x) {
  94.         return cdf((x - location) / scale);
  95.     }

  96.     /** {@inheritDoc} */
  97.     @Override
  98.     public double survivalProbability(double x) {
  99.         return cdf(-(x - location) / scale);
  100.     }

  101.     /**
  102.      * Compute the CDF of the Cauchy distribution with location 0 and scale 1.
  103.      * @param x Point at which the CDF is evaluated
  104.      * @return CDF(x)
  105.      */
  106.     private static double cdf(double x) {
  107.         return 0.5 + (Math.atan(x) / Math.PI);
  108.     }

  109.     /**
  110.      * {@inheritDoc}
  111.      *
  112.      * <p>Returns {@link Double#NEGATIVE_INFINITY} when {@code p == 0}
  113.      * and {@link Double#POSITIVE_INFINITY} when {@code p == 1}.
  114.      */
  115.     @Override
  116.     public double inverseCumulativeProbability(double p) {
  117.         ArgumentUtils.checkProbability(p);
  118.         if (p == 0) {
  119.             return Double.NEGATIVE_INFINITY;
  120.         } else  if (p == 1) {
  121.             return Double.POSITIVE_INFINITY;
  122.         }
  123.         return location + scale * Math.tan(Math.PI * (p - 0.5));
  124.     }

  125.     /**
  126.      * {@inheritDoc}
  127.      *
  128.      * <p>Returns {@link Double#NEGATIVE_INFINITY} when {@code p == 1}
  129.      * and {@link Double#POSITIVE_INFINITY} when {@code p == 0}.
  130.      */
  131.     @Override
  132.     public double inverseSurvivalProbability(double p) {
  133.         ArgumentUtils.checkProbability(p);
  134.         if (p == 1) {
  135.             return Double.NEGATIVE_INFINITY;
  136.         } else  if (p == 0) {
  137.             return Double.POSITIVE_INFINITY;
  138.         }
  139.         return location - scale * Math.tan(Math.PI * (p - 0.5));
  140.     }

  141.     /**
  142.      * {@inheritDoc}
  143.      *
  144.      * <p>The mean is always undefined.
  145.      *
  146.      * @return {@link Double#NaN NaN}.
  147.      */
  148.     @Override
  149.     public double getMean() {
  150.         return Double.NaN;
  151.     }

  152.     /**
  153.      * {@inheritDoc}
  154.      *
  155.      * <p>The variance is always undefined.
  156.      *
  157.      * @return {@link Double#NaN NaN}.
  158.      */
  159.     @Override
  160.     public double getVariance() {
  161.         return Double.NaN;
  162.     }

  163.     /**
  164.      * {@inheritDoc}
  165.      *
  166.      * <p>The lower bound of the support is always negative infinity.
  167.      *
  168.      * @return {@linkplain Double#NEGATIVE_INFINITY negative infinity}.
  169.      */
  170.     @Override
  171.     public double getSupportLowerBound() {
  172.         return Double.NEGATIVE_INFINITY;
  173.     }

  174.     /**
  175.      * {@inheritDoc}
  176.      *
  177.      * <p>The upper bound of the support is always positive infinity.
  178.      *
  179.      * @return {@linkplain Double#POSITIVE_INFINITY positive infinity}.
  180.      */
  181.     @Override
  182.     public double getSupportUpperBound() {
  183.         return Double.POSITIVE_INFINITY;
  184.     }

  185.     /** {@inheritDoc} */
  186.     @Override
  187.     double getMedian() {
  188.         // Overridden for the probability(double, double) method.
  189.         // This is intentionally not a public method.
  190.         return location;
  191.     }

  192.     /** {@inheritDoc} */
  193.     @Override
  194.     public ContinuousDistribution.Sampler createSampler(final UniformRandomProvider rng) {
  195.         // Cauchy distribution =
  196.         // Stable distribution with alpha=1, beta=0, gamma=scale, delta=location
  197.         return StableSampler.of(rng, 1, 0, getScale(), getLocation())::sample;
  198.     }
  199. }