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.rng.sampling.distribution;
018
019import org.apache.commons.rng.UniformRandomProvider;
020
021/**
022 * Distribution sampler that uses the
023 * <a href="https://en.wikipedia.org/wiki/Inverse_transform_sampling">
024 * inversion method</a>.
025 *
026 * It can be used to sample any distribution that provides access to its
027 * <em>inverse cumulative probability function</em>.
028 *
029 * <p>Sampling uses {@link UniformRandomProvider#nextDouble()}.</p>
030 *
031 * <p>Example:</p>
032 * <pre><code>
033 * import org.apache.commons.math3.distribution.IntegerDistribution;
034 * import org.apache.commons.math3.distribution.BinomialDistribution;
035 *
036 * import org.apache.commons.rng.simple.RandomSource;
037 * import org.apache.commons.rng.sampling.distribution.DiscreteSampler;
038 * import org.apache.commons.rng.sampling.distribution.InverseTransformDiscreteSampler;
039 * import org.apache.commons.rng.sampling.distribution.DiscreteInverseCumulativeProbabilityFunction;
040 *
041 * // Distribution to sample.
042 * final IntegerDistribution dist = new BinomialDistribution(11, 0.56);
043 * // Create the sampler.
044 * final DiscreteSampler binomialSampler =
045 *     InverseTransformDiscreteSampler.of(RandomSource.XO_RO_SHI_RO_128_PP.create(),
046 *                                        new DiscreteInverseCumulativeProbabilityFunction() {
047 *                                            public int inverseCumulativeProbability(double p) {
048 *                                                return dist.inverseCumulativeProbability(p);
049 *                                            }
050 *                                        });
051 *
052 * // Generate random deviate.
053 * int random = binomialSampler.sample();
054 * </code></pre>
055 *
056 * @since 1.0
057 */
058public class InverseTransformDiscreteSampler
059    extends SamplerBase
060    implements SharedStateDiscreteSampler {
061    /** Inverse cumulative probability function. */
062    private final DiscreteInverseCumulativeProbabilityFunction function;
063    /** Underlying source of randomness. */
064    private final UniformRandomProvider rng;
065
066    /**
067     * @param rng Generator of uniformly distributed random numbers.
068     * @param function Inverse cumulative probability function.
069     */
070    public InverseTransformDiscreteSampler(UniformRandomProvider rng,
071                                           DiscreteInverseCumulativeProbabilityFunction function) {
072        super(null);
073        this.rng = rng;
074        this.function = function;
075    }
076
077    /** {@inheritDoc} */
078    @Override
079    public int sample() {
080        return function.inverseCumulativeProbability(rng.nextDouble());
081    }
082
083    /** {@inheritDoc} */
084    @Override
085    public String toString() {
086        return function.toString() + " (inverse method) [" + rng.toString() + "]";
087    }
088
089    /**
090     * {@inheritDoc}
091     *
092     * <p>Note: The new sampler will share the inverse cumulative probability function. This
093     * must be suitable for concurrent use to ensure thread safety.</p>
094     *
095     * @since 1.3
096     */
097    @Override
098    public SharedStateDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
099        return new InverseTransformDiscreteSampler(rng, function);
100    }
101
102    /**
103     * Create a new inverse-transform discrete sampler.
104     *
105     * <p>To use the sampler to
106     * {@link org.apache.commons.rng.sampling.SharedStateSampler share state} the function must be
107     * suitable for concurrent use.</p>
108     *
109     * @param rng Generator of uniformly distributed random numbers.
110     * @param function Inverse cumulative probability function.
111     * @return the sampler
112     * @see #withUniformRandomProvider(UniformRandomProvider)
113     * @since 1.3
114     */
115    public static SharedStateDiscreteSampler of(UniformRandomProvider rng,
116                                                DiscreteInverseCumulativeProbabilityFunction function) {
117        return new InverseTransformDiscreteSampler(rng, function);
118    }
119}