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    
018    package org.apache.commons.math.distribution;
019    
020    import java.io.Serializable;
021    
022    import org.apache.commons.math.MathRuntimeException;
023    
024    /**
025     * Default implementation of
026     * {@link org.apache.commons.math.distribution.CauchyDistribution}.
027     *
028     * @since 1.1
029     * @version $Revision: 925900 $ $Date: 2010-03-21 17:10:07 -0400 (Sun, 21 Mar 2010) $
030     */
031    public class CauchyDistributionImpl extends AbstractContinuousDistribution
032            implements CauchyDistribution, Serializable {
033    
034        /**
035         * Default inverse cumulative probability accuracy
036         * @since 2.1
037         */
038        public static final double DEFAULT_INVERSE_ABSOLUTE_ACCURACY = 1e-9;
039    
040        /** Serializable version identifier */
041        private static final long serialVersionUID = 8589540077390120676L;
042    
043        /** The median of this distribution. */
044        private double median = 0;
045    
046        /** The scale of this distribution. */
047        private double scale = 1;
048    
049        /** Inverse cumulative probability accuracy */
050        private final double solverAbsoluteAccuracy;
051    
052        /**
053         * Creates cauchy distribution with the medain equal to zero and scale
054         * equal to one.
055         */
056        public CauchyDistributionImpl(){
057            this(0.0, 1.0);
058        }
059    
060        /**
061         * Create a cauchy distribution using the given median and scale.
062         * @param median median for this distribution
063         * @param s scale parameter for this distribution
064         */
065        public CauchyDistributionImpl(double median, double s){
066            this(median, s, DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
067        }
068    
069        /**
070         * Create a cauchy distribution using the given median and scale.
071         * @param median median for this distribution
072         * @param s scale parameter for this distribution
073         * @param inverseCumAccuracy the maximum absolute error in inverse cumulative probability estimates
074         * (defaults to {@link #DEFAULT_INVERSE_ABSOLUTE_ACCURACY})
075         * @since 2.1
076         */
077        public CauchyDistributionImpl(double median, double s, double inverseCumAccuracy) {
078            super();
079            setMedianInternal(median);
080            setScaleInternal(s);
081            solverAbsoluteAccuracy = inverseCumAccuracy;
082        }
083    
084        /**
085         * For this distribution, X, this method returns P(X &lt; <code>x</code>).
086         * @param x the value at which the CDF is evaluated.
087         * @return CDF evaluted at <code>x</code>.
088         */
089        public double cumulativeProbability(double x) {
090            return 0.5 + (Math.atan((x - median) / scale) / Math.PI);
091        }
092    
093        /**
094         * Access the median.
095         * @return median for this distribution
096         */
097        public double getMedian() {
098            return median;
099        }
100    
101        /**
102         * Access the scale parameter.
103         * @return scale parameter for this distribution
104         */
105        public double getScale() {
106            return scale;
107        }
108    
109        /**
110         * Returns the probability density for a particular point.
111         *
112         * @param x The point at which the density should be computed.
113         * @return The pdf at point x.
114         * @since 2.1
115         */
116        @Override
117        public double density(double x) {
118            final double dev = x - median;
119            return (1 / Math.PI) * (scale / (dev * dev + scale * scale));
120        }
121    
122        /**
123         * For this distribution, X, this method returns the critical point x, such
124         * that P(X &lt; x) = <code>p</code>.
125         * <p>
126         * Returns <code>Double.NEGATIVE_INFINITY</code> for p=0 and
127         * <code>Double.POSITIVE_INFINITY</code> for p=1.</p>
128         *
129         * @param p the desired probability
130         * @return x, such that P(X &lt; x) = <code>p</code>
131         * @throws IllegalArgumentException if <code>p</code> is not a valid
132         *         probability.
133         */
134        @Override
135        public double inverseCumulativeProbability(double p) {
136            double ret;
137            if (p < 0.0 || p > 1.0) {
138                throw MathRuntimeException.createIllegalArgumentException(
139                      "{0} out of [{1}, {2}] range", p, 0.0, 1.0);
140            } else if (p == 0) {
141                ret = Double.NEGATIVE_INFINITY;
142            } else  if (p == 1) {
143                ret = Double.POSITIVE_INFINITY;
144            } else {
145                ret = median + scale * Math.tan(Math.PI * (p - .5));
146            }
147            return ret;
148        }
149    
150        /**
151         * Modify the median.
152         * @param median for this distribution
153         * @deprecated as of 2.1 (class will become immutable in 3.0)
154         */
155        @Deprecated
156        public void setMedian(double median) {
157            setMedianInternal(median);
158        }
159        /**
160         * Modify the median.
161         * @param newMedian for this distribution
162         */
163        private void setMedianInternal(double newMedian) {
164            this.median = newMedian;
165        }
166    
167        /**
168         * Modify the scale parameter.
169         * @param s scale parameter for this distribution
170         * @throws IllegalArgumentException if <code>sd</code> is not positive.
171         * @deprecated as of 2.1 (class will become immutable in 3.0)
172         */
173        @Deprecated
174        public void setScale(double s) {
175            setScaleInternal(s);
176        }
177        /**
178         * Modify the scale parameter.
179         * @param s scale parameter for this distribution
180         * @throws IllegalArgumentException if <code>sd</code> is not positive.
181         */
182        private void setScaleInternal(double s) {
183            if (s <= 0.0) {
184                throw MathRuntimeException.createIllegalArgumentException(
185                      "scale must be positive ({0})", s);
186            }
187            scale = s;
188        }
189    
190        /**
191         * Access the domain value lower bound, based on <code>p</code>, used to
192         * bracket a CDF root.  This method is used by
193         * {@link #inverseCumulativeProbability(double)} to find critical values.
194         *
195         * @param p the desired probability for the critical value
196         * @return domain value lower bound, i.e.
197         *         P(X &lt; <i>lower bound</i>) &lt; <code>p</code>
198         */
199        @Override
200        protected double getDomainLowerBound(double p) {
201            double ret;
202    
203            if (p < .5) {
204                ret = -Double.MAX_VALUE;
205            } else {
206                ret = median;
207            }
208    
209            return ret;
210        }
211    
212        /**
213         * Access the domain value upper bound, based on <code>p</code>, used to
214         * bracket a CDF root.  This method is used by
215         * {@link #inverseCumulativeProbability(double)} to find critical values.
216         *
217         * @param p the desired probability for the critical value
218         * @return domain value upper bound, i.e.
219         *         P(X &lt; <i>upper bound</i>) &gt; <code>p</code>
220         */
221        @Override
222        protected double getDomainUpperBound(double p) {
223            double ret;
224    
225            if (p < .5) {
226                ret = median;
227            } else {
228                ret = Double.MAX_VALUE;
229            }
230    
231            return ret;
232        }
233    
234        /**
235         * Access the initial domain value, based on <code>p</code>, used to
236         * bracket a CDF root.  This method is used by
237         * {@link #inverseCumulativeProbability(double)} to find critical values.
238         *
239         * @param p the desired probability for the critical value
240         * @return initial domain value
241         */
242        @Override
243        protected double getInitialDomain(double p) {
244            double ret;
245    
246            if (p < .5) {
247                ret = median - scale;
248            } else if (p > .5) {
249                ret = median + scale;
250            } else {
251                ret = median;
252            }
253    
254            return ret;
255        }
256    
257        /**
258         * Return the absolute accuracy setting of the solver used to estimate
259         * inverse cumulative probabilities.
260         *
261         * @return the solver absolute accuracy
262         * @since 2.1
263         */
264        @Override
265        protected double getSolverAbsoluteAccuracy() {
266            return solverAbsoluteAccuracy;
267        }
268    }