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    @Override
113    public double inverseCumulativeProbability(double p) throws OutOfRangeException {
114        if (p < 0.0 || p > 1.0) {
115            throw new OutOfRangeException(p, 0.0, 1.0);
116        } else if (p == 0) {
117            return Double.NEGATIVE_INFINITY;
118        } else if (p == 1) {
119            return Double.POSITIVE_INFINITY;
120        }
121        double x = (p > 0.5) ? -Math.log(2.0 - 2.0 * p) : Math.log(2.0 * p);
122        return mu + beta * x;
123    }
124
125    /** {@inheritDoc} */
126    public double getNumericalMean() {
127        return mu;
128    }
129
130    /** {@inheritDoc} */
131    public double getNumericalVariance() {
132        return 2.0 * beta * beta;
133    }
134
135    /** {@inheritDoc} */
136    public double getSupportLowerBound() {
137        return Double.NEGATIVE_INFINITY;
138    }
139
140    /** {@inheritDoc} */
141    public double getSupportUpperBound() {
142        return Double.POSITIVE_INFINITY;
143    }
144
145    /** {@inheritDoc} */
146    public boolean isSupportLowerBoundInclusive() {
147        return false;
148    }
149
150    /** {@inheritDoc} */
151    public boolean isSupportUpperBoundInclusive() {
152        return false;
153    }
154
155    /** {@inheritDoc} */
156    public boolean isSupportConnected() {
157        return true;
158    }
159
160}