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 */
017package org.apache.commons.math3.distribution;
018
019import org.apache.commons.math3.exception.NotStrictlyPositiveException;
020import org.apache.commons.math3.exception.OutOfRangeException;
021import org.apache.commons.math3.exception.util.LocalizedFormats;
022import org.apache.commons.math3.random.RandomGenerator;
023import org.apache.commons.math3.random.Well19937c;
024import org.apache.commons.math3.util.FastMath;
025
026/**
027 * This class implements the Laplace distribution.
028 *
029 * @see <a href="http://en.wikipedia.org/wiki/Laplace_distribution">Laplace distribution (Wikipedia)</a>
030 *
031 * @since 3.4
032 */
033public class LaplaceDistribution extends AbstractRealDistribution {
034
035    /** Serializable version identifier. */
036    private static final long serialVersionUID = 20141003;
037
038    /** The location parameter. */
039    private final double mu;
040    /** The scale parameter. */
041    private final double beta;
042
043    /**
044     * Build a new instance.
045     * <p>
046     * <b>Note:</b> this constructor will implicitly create an instance of
047     * {@link Well19937c} as random generator to be used for sampling only (see
048     * {@link #sample()} and {@link #sample(int)}). In case no sampling is
049     * needed for the created distribution, it is advised to pass {@code null}
050     * as random generator via the appropriate constructors to avoid the
051     * additional initialisation overhead.
052     *
053     * @param mu location parameter
054     * @param beta scale parameter (must be positive)
055     * @throws NotStrictlyPositiveException if {@code beta <= 0}
056     */
057    public LaplaceDistribution(double mu, double beta) {
058        this(new Well19937c(), mu, beta);
059    }
060
061    /**
062     * Build a new instance.
063     *
064     * @param rng Random number generator
065     * @param mu location parameter
066     * @param beta scale parameter (must be positive)
067     * @throws NotStrictlyPositiveException if {@code beta <= 0}
068     */
069    public LaplaceDistribution(RandomGenerator rng, double mu, double beta) {
070        super(rng);
071
072        if (beta <= 0.0) {
073            throw new NotStrictlyPositiveException(LocalizedFormats.NOT_POSITIVE_SCALE, beta);
074        }
075
076        this.mu = mu;
077        this.beta = beta;
078    }
079
080    /**
081     * Access the location parameter, {@code mu}.
082     *
083     * @return the location parameter.
084     */
085    public double getLocation() {
086        return mu;
087    }
088
089    /**
090     * Access the scale parameter, {@code beta}.
091     *
092     * @return the scale parameter.
093     */
094    public double getScale() {
095        return beta;
096    }
097
098    /** {@inheritDoc} */
099    public double density(double x) {
100        return FastMath.exp(-FastMath.abs(x - mu) / beta) / (2.0 * beta);
101    }
102
103    /** {@inheritDoc} */
104    public double cumulativeProbability(double x) {
105        if (x <= mu) {
106            return FastMath.exp((x - mu) / beta) / 2.0;
107        } else {
108            return 1.0 - FastMath.exp((mu - x) / beta) / 2.0;
109        }
110    }
111
112    /** {@inheritDoc} */
113    @Override
114    public double inverseCumulativeProbability(double p) throws OutOfRangeException {
115        if (p < 0.0 || p > 1.0) {
116            throw new OutOfRangeException(p, 0.0, 1.0);
117        } else if (p == 0) {
118            return Double.NEGATIVE_INFINITY;
119        } else if (p == 1) {
120            return Double.POSITIVE_INFINITY;
121        }
122        double x = (p > 0.5) ? -Math.log(2.0 - 2.0 * p) : Math.log(2.0 * p);
123        return mu + beta * x;
124    }
125
126    /** {@inheritDoc} */
127    public double getNumericalMean() {
128        return mu;
129    }
130
131    /** {@inheritDoc} */
132    public double getNumericalVariance() {
133        return 2.0 * beta * beta;
134    }
135
136    /** {@inheritDoc} */
137    public double getSupportLowerBound() {
138        return Double.NEGATIVE_INFINITY;
139    }
140
141    /** {@inheritDoc} */
142    public double getSupportUpperBound() {
143        return Double.POSITIVE_INFINITY;
144    }
145
146    /** {@inheritDoc} */
147    public boolean isSupportLowerBoundInclusive() {
148        return false;
149    }
150
151    /** {@inheritDoc} */
152    public boolean isSupportUpperBoundInclusive() {
153        return false;
154    }
155
156    /** {@inheritDoc} */
157    public boolean isSupportConnected() {
158        return true;
159    }
160
161}