This is an automated email from the ASF dual-hosted git repository. aherbert pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-statistics.git
commit 5b5717d2b8d60eec498750b61153d372e7c83212 Author: aherbert <aherb...@apache.org> AuthorDate: Mon Oct 3 12:02:22 2022 +0100 Use TSampler from commons RNG --- .../statistics/distribution/TDistribution.java | 98 +--------------------- 1 file changed, 3 insertions(+), 95 deletions(-) diff --git a/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/TDistribution.java b/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/TDistribution.java index cd3c2b2..e828276 100644 --- a/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/TDistribution.java +++ b/commons-statistics-distribution/src/main/java/org/apache/commons/statistics/distribution/TDistribution.java @@ -18,7 +18,7 @@ package org.apache.commons.statistics.distribution; import org.apache.commons.numbers.gamma.RegularizedBeta; import org.apache.commons.rng.UniformRandomProvider; -import java.util.function.DoubleUnaryOperator; +import org.apache.commons.rng.sampling.distribution.TSampler; import org.apache.commons.numbers.gamma.Beta; import org.apache.commons.numbers.gamma.LogBeta; @@ -231,100 +231,8 @@ public abstract class TDistribution extends AbstractContinuousDistribution { @Override public Sampler createSampler(UniformRandomProvider rng) { - // TODO: Move sampler to Commons RNG - return new TSampler(rng, getDegreesOfFreedom()); - } - - /** - * Sampling from a T-distribution. - * <blockquote> - * Bailey, R. W. (1994) - * Polar Generation of Random Variates with the t-Distribution. - * Mathematics of Computation 62, 779-781. - * </blockquote> - * @see <a href="https://doi.org/10.2307/2153537">Mathematics of Computation, 62, 779-781</a> - */ - private static class TSampler implements Sampler { - /** Threshold for large degrees of freedom. */ - private static final double LARGE_DF = 25; - /** The multiplier to convert the least significant 53-bits of a {@code long} to a - * uniform {@code double}. */ - private static final double DOUBLE_MULTIPLIER = 0x1.0p-53; - - /** Source of randomness. */ - private final UniformRandomProvider rng; - /** Degrees of freedom. */ - private final double df; - /** Function to compute pow(x, -2/v) - 1, where v = degrees of freedom. */ - private final DoubleUnaryOperator powm1; - - /** - * @param rng Source of randomness. - * @param v Degrees of freedom. - */ - TSampler(UniformRandomProvider rng, double v) { - this.rng = rng; - df = v; - - // The sampler requires pow(w, -2/v) - 1 with - // 0 <= w <= 1; Expected(w) = sqrt(0.5). - // When the exponent is small then pow(x, y) -> 1. - // This affects large degrees of freedom. - final double exponent = -2 / v; - powm1 = v > LARGE_DF ? - x -> Math.expm1(Math.log(x) * exponent) : - x -> Math.pow(x, exponent) - 1; - } - - @Override - public double sample() { - // Require u and v in [0, 1] and a random sign. - // Create u in (0, 1] to avoid generating nan - // from u*u/w (0/0) or r2*c2 (inf*0). - final double u = makeNonZeroDouble(rng.nextLong()); - final double v = makeSignedDouble(rng.nextLong()); - final double w = u * u + v * v; - if (w > 1) { - // Rejection frequency = 1 - pi/4 = 0.215. - // Recursion will generate stack overflow given a broken RNG - // and avoids an infinite loop. - return sample(); - } - // Sidestep a square-root calculation. - final double c2 = u * u / w; - final double r2 = df * powm1.applyAsDouble(w); - // Choose sign at random from the sign of v. - return Math.copySign(Math.sqrt(r2 * c2), v); - } - - /** - * Creates a {@code double} in the interval {@code (0, 1]} from a {@code long} value. - * - * @param v Number. - * @return {@code (0, 1]}. - */ - private static double makeNonZeroDouble(long v) { - // This matches the method in o.a.c.rng.core.util.NumberFactory.makeDouble(long) - // but shifts the range from [0, 1) to (0, 1]. - return ((v >>> 11) + 1L) * DOUBLE_MULTIPLIER; - } - - /** - * Creates a signed double in the range {@code [-1, 1)}. - * - * <p>Note: This method will not return samples for both -0.0 and 0.0. - * - * @param bits the bits - * @return {@code [-1, 1)}. - */ - private static double makeSignedDouble(long bits) { - // As per o.a.c.rng.core.utils.NumberFactory.makeDouble(long) but using a signed - // shift of 10 in place of an unsigned shift of 11. - // Use the upper 54 bits on the assumption they are more random. - // The sign bit is maintained by the signed shift. - // The next 53 bits generates a magnitude in the range [0, 2^53) or [-2^53, 0). - return (bits >> 10) * DOUBLE_MULTIPLIER; - } + // T distribution sampler. + return TSampler.of(rng, getDegreesOfFreedom())::sample; } }