    001    /*
    002     * Licensed to the Apache Software Foundation (ASF) under one or more
    003     * contributor license agreements.  See the NOTICE file distributed with
    004     * this work for additional information regarding copyright ownership.
    005     * The ASF licenses this file to You under the Apache License, Version 2.0
    006     * (the "License"); you may not use this file except in compliance with
    007     * the License.  You may obtain a copy of the License at
    008     *
    009     *      http://www.apache.org/licenses/LICENSE-2.0
    010     *
    011     * Unless required by applicable law or agreed to in writing, software
    012     * distributed under the License is distributed on an "AS IS" BASIS,
    013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014     * See the License for the specific language governing permissions and
    015     * limitations under the License.
    016     */
    017    package org.apache.commons.math3.distribution;
    018
    019    import org.apache.commons.math3.exception.NotStrictlyPositiveException;
    020    import org.apache.commons.math3.exception.OutOfRangeException;
    021    import org.apache.commons.math3.exception.util.LocalizedFormats;
    022    import org.apache.commons.math3.util.FastMath;
    023    import org.apache.commons.math3.random.RandomGenerator;
    024    import org.apache.commons.math3.random.Well19937c;
    025
    026    /**
    027     * Implementation of the Cauchy distribution.
    028     *
    029     * @see <a href="http://en.wikipedia.org/wiki/Cauchy_distribution">Cauchy distribution (Wikipedia)</a>
    030     * @see <a href="http://mathworld.wolfram.com/CauchyDistribution.html">Cauchy Distribution (MathWorld)</a>
    031     * @since 1.1 (changed to concrete class in 3.0)
    032     * @version $Id: CauchyDistribution.java 1416643 2012-12-03 19:37:14Z tn $
    033     */
    034    public class CauchyDistribution extends AbstractRealDistribution {
    035        /**
    036         * Default inverse cumulative probability accuracy.
    037         * @since 2.1
    038         */
    039        public static final double DEFAULT_INVERSE_ABSOLUTE_ACCURACY = 1e-9;
    040        /** Serializable version identifier */
    041        private static final long serialVersionUID = 8589540077390120676L;
    042        /** The median of this distribution. */
    043        private final double median;
    044        /** The scale of this distribution. */
    045        private final double scale;
    046        /** Inverse cumulative probability accuracy */
    047        private final double solverAbsoluteAccuracy;
    048
    049        /**
    050         * Creates a Cauchy distribution with the median equal to zero and scale
    051         * equal to one.
    052         */
    053        public CauchyDistribution() {
    054            this(0, 1);
    055        }
    056
    057        /**
    058         * Creates a Cauchy distribution using the given median and scale.
    059         *
    060         * @param median Median for this distribution.
    061         * @param scale Scale parameter for this distribution.
    062         */
    063        public CauchyDistribution(double median, double scale) {
    064            this(median, scale, DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
    065        }
    066
    067        /**
    068         * Creates a Cauchy distribution using the given median and scale.
    069         *
    070         * @param median Median for this distribution.
    071         * @param scale Scale parameter for this distribution.
    072         * @param inverseCumAccuracy Maximum absolute error in inverse
    073         * cumulative probability estimates
    074         * (defaults to {@link #DEFAULT_INVERSE_ABSOLUTE_ACCURACY}).
    075         * @throws NotStrictlyPositiveException if {@code scale <= 0}.
    076         * @since 2.1
    077         */
    078        public CauchyDistribution(double median, double scale,
    079                                  double inverseCumAccuracy) {
    080            this(new Well19937c(), median, scale, inverseCumAccuracy);
    081        }
    082
    083        /**
    084         * Creates a Cauchy distribution.
    085         *
    086         * @param rng Random number generator.
    087         * @param median Median for this distribution.
    088         * @param scale Scale parameter for this distribution.
    089         * @param inverseCumAccuracy Maximum absolute error in inverse
    090         * cumulative probability estimates
    091         * (defaults to {@link #DEFAULT_INVERSE_ABSOLUTE_ACCURACY}).
    092         * @throws NotStrictlyPositiveException if {@code scale <= 0}.
    093         * @since 3.1
    094         */
    095        public CauchyDistribution(RandomGenerator rng,
    096                                  double median,
    097                                  double scale,
    098                                  double inverseCumAccuracy) {
    099            super(rng);
    100            if (scale <= 0) {
    101                throw new NotStrictlyPositiveException(LocalizedFormats.SCALE, scale);
    102            }
    103            this.scale = scale;
    104            this.median = median;
    105            solverAbsoluteAccuracy = inverseCumAccuracy;
    106        }
    107
    108        /** {@inheritDoc} */
    109        public double cumulativeProbability(double x) {
    110            return 0.5 + (FastMath.atan((x - median) / scale) / FastMath.PI);
    111        }
    112
    113        /**
    114         * Access the median.
    115         *
    116         * @return the median for this distribution.
    117         */
    118        public double getMedian() {
    119            return median;
    120        }
    121
    122        /**
    123         * Access the scale parameter.
    124         *
    125         * @return the scale parameter for this distribution.
    126         */
    127        public double getScale() {
    128            return scale;
    129        }
    130
    131        /** {@inheritDoc} */
    132        public double density(double x) {
    133            final double dev = x - median;
    134            return (1 / FastMath.PI) * (scale / (dev * dev + scale * scale));
    135        }
    136
    137        /**
    138         * {@inheritDoc}
    139         *
    140         * Returns {@code Double.NEGATIVE_INFINITY} when {@code p == 0}
    141         * and {@code Double.POSITIVE_INFINITY} when {@code p == 1}.
    142         */
    143        @Override
    144        public double inverseCumulativeProbability(double p) throws OutOfRangeException {
    145            double ret;
    146            if (p < 0 || p > 1) {
    147                throw new OutOfRangeException(p, 0, 1);
    148            } else if (p == 0) {
    149                ret = Double.NEGATIVE_INFINITY;
    150            } else  if (p == 1) {
    151                ret = Double.POSITIVE_INFINITY;
    152            } else {
    153                ret = median + scale * FastMath.tan(FastMath.PI * (p - .5));
    154            }
    155            return ret;
    156        }
    157
    158        /** {@inheritDoc} */
    159        @Override
    160        protected double getSolverAbsoluteAccuracy() {
    161            return solverAbsoluteAccuracy;
    162        }
    163
    164        /**
    165         * {@inheritDoc}
    166         *
    167         * The mean is always undefined no matter the parameters.
    168         *
    169         * @return mean (always Double.NaN)
    170         */
    171        public double getNumericalMean() {
    172            return Double.NaN;
    173        }
    174
    175        /**
    176         * {@inheritDoc}
    177         *
    178         * The variance is always undefined no matter the parameters.
    179         *
    180         * @return variance (always Double.NaN)
    181         */
    182        public double getNumericalVariance() {
    183            return Double.NaN;
    184        }
    185
    186        /**
    187         * {@inheritDoc}
    188         *
    189         * The lower bound of the support is always negative infinity no matter
    190         * the parameters.
    191         *
    192         * @return lower bound of the support (always Double.NEGATIVE_INFINITY)
    193         */
    194        public double getSupportLowerBound() {
    195            return Double.NEGATIVE_INFINITY;
    196        }
    197
    198        /**
    199         * {@inheritDoc}
    200         *
    201         * The upper bound of the support is always positive infinity no matter
    202         * the parameters.
    203         *
    204         * @return upper bound of the support (always Double.POSITIVE_INFINITY)
    205         */
    206        public double getSupportUpperBound() {
    207            return Double.POSITIVE_INFINITY;
    208        }
    209
    210        /** {@inheritDoc} */
    211        public boolean isSupportLowerBoundInclusive() {
    212            return false;
    213        }
    214
    215        /** {@inheritDoc} */
    216        public boolean isSupportUpperBoundInclusive() {
    217            return false;
    218        }
    219
    220        /**
    221         * {@inheritDoc}
    222         *
    223         * The support of this distribution is connected.
    224         *
    225         * @return {@code true}
    226         */
    227        public boolean isSupportConnected() {
    228            return true;
    229        }
    230    }