/* * File: CauchyDistribution.java * Authors: Kevin R. Dixon * Company: Sandia National Laboratories * Project: Cognitive Foundry * * Copyright Feb 25, 2010, Sandia Corporation. * Under the terms of Contract DE-AC04-94AL85000, there is a non-exclusive * license for use of this work by or on behalf of the U.S. Government. * Export of this program may require a license from the United States * Government. See CopyrightHistory.txt for complete details. * */ package gov.sandia.cognition.statistics.distribution; import gov.sandia.cognition.annotation.PublicationReference; import gov.sandia.cognition.annotation.PublicationType; import gov.sandia.cognition.math.matrix.Vector; import gov.sandia.cognition.math.matrix.VectorFactory; import gov.sandia.cognition.statistics.AbstractClosedFormSmoothUnivariateDistribution; import gov.sandia.cognition.statistics.UnivariateProbabilityDensityFunction; import gov.sandia.cognition.statistics.SmoothCumulativeDistributionFunction; import java.util.ArrayList; import java.util.Random; /** * A Cauchy Distribution is the ratio of two Gaussian Distributions, sometimes * known as the Lorentz distribution. The mean is undefined and it has * infinite variance. * @author Kevin R. Dixon * @since 3.0 */ @PublicationReference( author="Wikipedia", title="Cauchy distribution", type=PublicationType.WebPage, year=2010, url="http://en.wikipedia.org/wiki/Cauchy_distribution" ) public class CauchyDistribution extends AbstractClosedFormSmoothUnivariateDistribution { /** * Default location, {@value}. */ public static final double DEFAULT_LOCATION = 0.0; /** * Default scale, {@value}. */ public static final double DEFAULT_SCALE = 1.0; /** * Central location (also the median and mode) of the distribution. */ protected double location; /** * Scale of the distribution, must be greater than zero. */ protected double scale; /** * Creates a new instance of CauchyDistribution */ public CauchyDistribution() { this( DEFAULT_LOCATION, DEFAULT_SCALE ); } /** * Creates a new instance of CauchyDistribution * @param location * Central location (also the median and mode) of the distribution. * @param scale * Scale of the distribution, must be greater than zero. */ public CauchyDistribution( final double location, final double scale) { this.location = location; this.scale = scale; } /** * Copy constructor * @param other * CauchyDistribution to copy. */ public CauchyDistribution( final CauchyDistribution other ) { this( other.getLocation(), other.getScale() ); } @Override public CauchyDistribution clone() { CauchyDistribution clone = (CauchyDistribution) super.clone(); return clone; } /** * Getter for location. * @return * Central location (also the median and mode) of the distribution. */ public double getLocation() { return this.location; } /** * Setter for location * @param location * Central location (also the median and mode) of the distribution. */ public void setLocation( final double location) { this.location = location; } /** * Getter for scale. * @return * Scale of the distribution, must be greater than zero. */ public double getScale() { return this.scale; } /** * Setter for scale * @param scale * Scale of the distribution, must be greater than zero. */ public void setScale( final double scale) { if( scale <= 0.0 ) { throw new IllegalArgumentException( "Scale must be > 0.0" ); } this.scale = scale; } @Override public Double getMean() { // The mean of a Cauchy is undefined due to its heavy tails. // However, a Cauchy distribution is symmetric about the "location" // parameter. So, one common-sense interpretation of the mean is that // it's equal to the median of a symmetric distribution. // So that's what I'm going with. return this.getLocation(); } @Override public double getMeanAsDouble() { return this.getLocation(); } @Override public double sampleAsDouble( final Random random) { double g1 = random.nextGaussian(); double g2 = random.nextGaussian(); double ratio = g1/g2; double scaled = ratio * this.scale; return scaled + this.location; } @Override public void sampleInto( final Random random, final double[] output, final int start, final int length) { final int end = start + length; for (int i = start; i < end; i++) { output[i] = this.sampleAsDouble(random); } } @Override public CauchyDistribution.CDF getCDF() { return new CauchyDistribution.CDF( this ); } @Override public Vector convertToVector() { return VectorFactory.getDefault().copyValues( this.getLocation(), this.getScale() ); } @Override public void convertFromVector( final Vector parameters) { parameters.assertDimensionalityEquals(2); this.setLocation( parameters.getElement(0) ); this.setScale( parameters.getElement(1) ); } @Override public double getVariance() { return Double.POSITIVE_INFINITY; } @Override public CauchyDistribution.PDF getProbabilityFunction() { return new CauchyDistribution.PDF( this ); } @Override public Double getMinSupport() { return Double.NEGATIVE_INFINITY; } @Override public Double getMaxSupport() { return Double.POSITIVE_INFINITY; } /** * PDF of the CauchyDistribution. */ public static class PDF extends CauchyDistribution implements UnivariateProbabilityDensityFunction { /** * Creates a new instance of CauchyDistribution */ public PDF() { super( DEFAULT_LOCATION, DEFAULT_SCALE ); } /** * Creates a new instance of CauchyDistribution * @param location * Central location (also the median and mode) of the distribution. * @param scale * Scale of the distribution, must be greater than zero. */ public PDF( final double location, final double scale) { super( location, scale ); } /** * Copy constructor * @param other * CauchyDistribution to copy. */ public PDF( final CauchyDistribution other ) { super( other ); } @Override public double logEvaluate( final Double input) { return this.logEvaluate((double) input); } @Override public double logEvaluate( final double input) { return Math.log(this.evaluate(input)); } @Override public Double evaluate( final Double input) { return this.evaluate( input.doubleValue() ); } @Override public double evaluateAsDouble( final Double input) { return this.evaluate(input.doubleValue()); } @Override public double evaluate( final double input) { final double leading = Math.PI * this.scale; final double d1 = (input - this.location) / this.scale; final double dx = 1.0 + d1*d1; final double denominator = leading * dx; return 1.0/denominator; } @Override public CauchyDistribution.PDF getProbabilityFunction() { return this; } } /** * CDF of the CauchyDistribution. */ public static class CDF extends CauchyDistribution implements SmoothCumulativeDistributionFunction { /** * Creates a new instance of CauchyDistribution */ public CDF() { super( DEFAULT_LOCATION, DEFAULT_SCALE ); } /** * Creates a new instance of CauchyDistribution * @param location * Central location (also the median and mode) of the distribution. * @param scale * Scale of the distribution, must be greater than zero. */ public CDF( final double location, final double scale) { super( location, scale ); } /** * Copy constructor * @param other * CauchyDistribution to copy. */ public CDF( final CauchyDistribution other ) { super( other ); } @Override public Double evaluate( final Double input) { return this.evaluate( input.doubleValue() ); } @Override public double evaluateAsDouble( final Double input) { return this.evaluate(input.doubleValue()); } @Override public double evaluate( final double input) { double d1 = Math.atan( (input - this.location)/this.scale ); return d1/Math.PI + 0.5; } @Override public CauchyDistribution.CDF getCDF() { return this; } @Override public CauchyDistribution.PDF getDerivative() { return this.getProbabilityFunction(); } @Override public Double differentiate( final Double input) { return this.getDerivative().evaluate(input); } } }