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     * Create an instance.
068     *
069     * @param rng Generator of uniformly distributed random numbers.
070     * @param function Inverse cumulative probability function.
071     */
072    public InverseTransformDiscreteSampler(UniformRandomProvider rng,
073                                           DiscreteInverseCumulativeProbabilityFunction function) {
074        super(null);
075        this.rng = rng;
076        this.function = function;
077    }
078
079    /** {@inheritDoc} */
080    @Override
081    public int sample() {
082        return function.inverseCumulativeProbability(rng.nextDouble());
083    }
084
085    /** {@inheritDoc} */
086    @Override
087    public String toString() {
088        return function.toString() + " (inverse method) [" + rng.toString() + "]";
089    }
090
091    /**
092     * {@inheritDoc}
093     *
094     * <p>Note: The new sampler will share the inverse cumulative probability function. This
095     * must be suitable for concurrent use to ensure thread safety.</p>
096     *
097     * @since 1.3
098     */
099    @Override
100    public SharedStateDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
101        return new InverseTransformDiscreteSampler(rng, function);
102    }
103
104    /**
105     * Create a new inverse-transform discrete sampler.
106     *
107     * <p>To use the sampler to
108     * {@link org.apache.commons.rng.sampling.SharedStateSampler share state} the function must be
109     * suitable for concurrent use.</p>
110     *
111     * @param rng Generator of uniformly distributed random numbers.
112     * @param function Inverse cumulative probability function.
113     * @return the sampler
114     * @see #withUniformRandomProvider(UniformRandomProvider)
115     * @since 1.3
116     */
117    public static SharedStateDiscreteSampler of(UniformRandomProvider rng,
118                                                DiscreteInverseCumulativeProbabilityFunction function) {
119        return new InverseTransformDiscreteSampler(rng, function);
120    }
121}