All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.commons.rng.sampling.distribution.ChengBetaSampler Maven / Gradle / Ivy

Go to download

The Apache Commons RNG Sampling module provides samplers for various distributions.

There is a newer version: 1.6
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.commons.rng.sampling.distribution;

import org.apache.commons.rng.UniformRandomProvider;

/**
 * Sampling from a beta distribution.
 *
 * 

Uses Cheng's algorithms for beta distribution sampling:

* *
*
 * R. C. H. Cheng,
 * "Generating beta variates with nonintegral shape parameters",
 * Communications of the ACM, 21, 317-322, 1978.
 * 
*
* *

Sampling uses {@link UniformRandomProvider#nextDouble()}.

* * @since 1.0 */ public class ChengBetaSampler extends SamplerBase implements SharedStateContinuousSampler { /** Natural logarithm of 4. */ private static final double LN_4 = Math.log(4.0); /** The appropriate beta sampler for the parameters. */ private final SharedStateContinuousSampler delegate; /** * Base class to implement Cheng's algorithms for the beta distribution. */ private abstract static class BaseChengBetaSampler implements SharedStateContinuousSampler { /** * Flag set to true if {@code a} is the beta distribution alpha shape parameter. * Otherwise {@code a} is the beta distribution beta shape parameter. * *

From the original Cheng paper this is equal to the result of: {@code a == a0}.

*/ protected final boolean aIsAlphaShape; /** * First shape parameter. * The meaning of this is dependent on the {@code aIsAlphaShape} flag. */ protected final double a; /** * Second shape parameter. * The meaning of this is dependent on the {@code aIsAlphaShape} flag. */ protected final double b; /** Underlying source of randomness. */ protected final UniformRandomProvider rng; /** * The algorithm alpha factor. This is not the beta distribution alpha shape parameter. * It is the sum of the two shape parameters ({@code a + b}. */ protected final double alpha; /** The logarithm of the alpha factor. */ protected final double logAlpha; /** * @param rng Generator of uniformly distributed random numbers. * @param aIsAlphaShape true if {@code a} is the beta distribution alpha shape parameter. * @param a Distribution first shape parameter. * @param b Distribution second shape parameter. */ BaseChengBetaSampler(UniformRandomProvider rng, boolean aIsAlphaShape, double a, double b) { this.rng = rng; this.aIsAlphaShape = aIsAlphaShape; this.a = a; this.b = b; alpha = a + b; logAlpha = Math.log(alpha); } /** * @param rng Generator of uniformly distributed random numbers. * @param source Source to copy. */ private BaseChengBetaSampler(UniformRandomProvider rng, BaseChengBetaSampler source) { this.rng = rng; aIsAlphaShape = source.aIsAlphaShape; a = source.a; b = source.b; // Compute algorithm factors. alpha = source.alpha; logAlpha = source.logAlpha; } /** {@inheritDoc} */ @Override public String toString() { return "Cheng Beta deviate [" + rng.toString() + "]"; } /** * Compute the sample result X. * *
* If a == a0 deliver X = W/(b + W); otherwise deliver X = b/(b + W). *
* *

The finalisation step is shared between the BB and BC algorithm (as step 5 of the * BB algorithm and step 6 of the BC algorithm).

* * @param w Algorithm value W. * @return the sample value */ protected double computeX(double w) { // Avoid (infinity / infinity) producing NaN final double tmp = Math.min(w, Double.MAX_VALUE); return aIsAlphaShape ? tmp / (b + tmp) : b / (b + tmp); } } /** * Computes one sample using Cheng's BB algorithm, when beta distribution {@code alpha} and * {@code beta} shape parameters are both larger than 1. */ private static class ChengBBBetaSampler extends BaseChengBetaSampler { /** 1 + natural logarithm of 5. */ private static final double LN_5_P1 = 1 + Math.log(5.0); /** The algorithm beta factor. This is not the beta distribution beta shape parameter. */ private final double beta; /** The algorithm gamma factor. */ private final double gamma; /** * @param rng Generator of uniformly distributed random numbers. * @param aIsAlphaShape true if {@code a} is the beta distribution alpha shape parameter. * @param a min(alpha, beta) shape parameter. * @param b max(alpha, beta) shape parameter. */ ChengBBBetaSampler(UniformRandomProvider rng, boolean aIsAlphaShape, double a, double b) { super(rng, aIsAlphaShape, a, b); beta = Math.sqrt((alpha - 2) / (2 * a * b - alpha)); gamma = a + 1 / beta; } /** * @param rng Generator of uniformly distributed random numbers. * @param source Source to copy. */ private ChengBBBetaSampler(UniformRandomProvider rng, ChengBBBetaSampler source) { super(rng, source); // Compute algorithm factors. beta = source.beta; gamma = source.gamma; } @Override public double sample() { double r; double w; double t; do { // Step 1: final double u1 = rng.nextDouble(); final double u2 = rng.nextDouble(); final double v = beta * (Math.log(u1) - Math.log1p(-u1)); w = a * Math.exp(v); final double z = u1 * u1 * u2; r = gamma * v - LN_4; final double s = a + r - w; // Step 2: if (s + LN_5_P1 >= 5 * z) { break; } // Step 3: t = Math.log(z); if (s >= t) { break; } // Step 4: } while (r + alpha * (logAlpha - Math.log(b + w)) < t); // Step 5: return computeX(w); } @Override public SharedStateContinuousSampler withUniformRandomProvider(UniformRandomProvider rng) { return new ChengBBBetaSampler(rng, this); } } /** * Computes one sample using Cheng's BC algorithm, when at least one of beta distribution * {@code alpha} or {@code beta} shape parameters is smaller than 1. */ private static class ChengBCBetaSampler extends BaseChengBetaSampler { /** 1/2. */ private static final double ONE_HALF = 1d / 2; /** 1/4. */ private static final double ONE_QUARTER = 1d / 4; /** The algorithm beta factor. This is not the beta distribution beta shape parameter. */ private final double beta; /** The algorithm delta factor. */ private final double delta; /** The algorithm k1 factor. */ private final double k1; /** The algorithm k2 factor. */ private final double k2; /** * @param rng Generator of uniformly distributed random numbers. * @param aIsAlphaShape true if {@code a} is the beta distribution alpha shape parameter. * @param a max(alpha, beta) shape parameter. * @param b min(alpha, beta) shape parameter. */ ChengBCBetaSampler(UniformRandomProvider rng, boolean aIsAlphaShape, double a, double b) { super(rng, aIsAlphaShape, a, b); // Compute algorithm factors. beta = 1 / b; delta = 1 + a - b; // The following factors are divided by 4: // k1 = delta * (1 + 3b) / (18a/b - 14) // k2 = 1 + (2 + 1/delta) * b k1 = delta * (1.0 / 72.0 + 3.0 / 72.0 * b) / (a * beta - 7.0 / 9.0); k2 = 0.25 + (0.5 + 0.25 / delta) * b; } /** * @param rng Generator of uniformly distributed random numbers. * @param source Source to copy. */ private ChengBCBetaSampler(UniformRandomProvider rng, ChengBCBetaSampler source) { super(rng, source); beta = source.beta; delta = source.delta; k1 = source.k1; k2 = source.k2; } @Override public double sample() { double w; while (true) { // Step 1: final double u1 = rng.nextDouble(); final double u2 = rng.nextDouble(); // Compute Y and Z final double y = u1 * u2; final double z = u1 * y; if (u1 < ONE_HALF) { // Step 2: if (ONE_QUARTER * u2 + z - y >= k1) { continue; } } else { // Step 3: if (z <= ONE_QUARTER) { final double v = beta * (Math.log(u1) - Math.log1p(-u1)); w = a * Math.exp(v); break; } // Step 4: if (z >= k2) { continue; } } // Step 5: final double v = beta * (Math.log(u1) - Math.log1p(-u1)); w = a * Math.exp(v); if (alpha * (logAlpha - Math.log(b + w) + v) - LN_4 >= Math.log(z)) { break; } } // Step 6: return computeX(w); } @Override public SharedStateContinuousSampler withUniformRandomProvider(UniformRandomProvider rng) { return new ChengBCBetaSampler(rng, this); } } /** * Creates a sampler instance. * * @param rng Generator of uniformly distributed random numbers. * @param alpha Distribution first shape parameter. * @param beta Distribution second shape parameter. * @throws IllegalArgumentException if {@code alpha <= 0} or {@code beta <= 0} */ public ChengBetaSampler(UniformRandomProvider rng, double alpha, double beta) { super(null); delegate = of(rng, alpha, beta); } /** {@inheritDoc} */ @Override public double sample() { return delegate.sample(); } /** {@inheritDoc} */ @Override public String toString() { return delegate.toString(); } /** * {@inheritDoc} * * @since 1.3 */ @Override public SharedStateContinuousSampler withUniformRandomProvider(UniformRandomProvider rng) { return delegate.withUniformRandomProvider(rng); } /** * Creates a new beta distribution sampler. * * @param rng Generator of uniformly distributed random numbers. * @param alpha Distribution first shape parameter. * @param beta Distribution second shape parameter. * @return the sampler * @throws IllegalArgumentException if {@code alpha <= 0} or {@code beta <= 0} * @since 1.3 */ public static SharedStateContinuousSampler of(UniformRandomProvider rng, double alpha, double beta) { if (alpha <= 0) { throw new IllegalArgumentException("alpha is not strictly positive: " + alpha); } if (beta <= 0) { throw new IllegalArgumentException("beta is not strictly positive: " + beta); } // Choose the algorithm. final double a = Math.min(alpha, beta); final double b = Math.max(alpha, beta); final boolean aIsAlphaShape = a == alpha; return a > 1 ? // BB algorithm new ChengBBBetaSampler(rng, aIsAlphaShape, a, b) : // The BC algorithm is deliberately invoked with reversed parameters // as the argument order is: max(alpha,beta), min(alpha,beta). // Also invert the 'a is alpha' flag. new ChengBCBetaSampler(rng, !aIsAlphaShape, b, a); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy