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

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

Go to download

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

The 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 java.util.function.DoubleUnaryOperator;
import org.apache.commons.rng.UniformRandomProvider;

/**
 * Sampling from a T distribution.
 *
 * 

Uses Bailey's algorithm for t-distribution sampling:

* *
*
 * Bailey, R. W. (1994)
 * "Polar Generation of Random Variates with the t-Distribution."
 * Mathematics of Computation 62, 779-781.
 * 
*
* *

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

* * @see Student's T distribution (wikipedia) * @see Mathematics of Computation, 62, 779-781 * @since 1.5 */ public abstract class TSampler implements SharedStateContinuousSampler { /** Threshold for huge degrees of freedom. Above this value the CDF of the t distribution * matches the normal distribution. Value is 2/eps (where eps is the machine epsilon) * or approximately 9.0e15. */ private static final double HUGE_DF = 0x1.0p53; /** Source of randomness. */ private final UniformRandomProvider rng; /** * Sample from a t-distribution using Bailey's algorithm. */ private static final class StudentsTSampler extends TSampler { /** 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; /** 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 Generator of uniformly distributed random numbers. * @param v Degrees of freedom. */ StudentsTSampler(UniformRandomProvider rng, double v) { super(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; } /** * @param rng Generator of uniformly distributed random numbers. * @param source Source to copy. */ private StudentsTSampler(UniformRandomProvider rng, StudentsTSampler source) { super(rng); df = source.df; powm1 = source.powm1; } /** {@inheritDoc} */ @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 = InternalUtils.makeNonZeroDouble(nextLong()); final double v = makeSignedDouble(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); } /** {@inheritDoc} */ @Override public StudentsTSampler withUniformRandomProvider(UniformRandomProvider rng) { return new StudentsTSampler(rng, this); } /** * Creates a signed double in the range {@code [-1, 1)}. The magnitude is sampled evenly * from the 254 dyadic rationals in the range. * *

Note: This method will not return samples for both -0.0 and 0.0. * * @param bits the bits * @return the double */ 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; } } /** * Sample from a t-distribution using a normal distribution. * This is used when the degrees of freedom is extremely large (e.g. {@code > 1e16}). */ private static final class NormalTSampler extends TSampler { /** Underlying normalized Gaussian sampler. */ private final NormalizedGaussianSampler sampler; /** * @param rng Generator of uniformly distributed random numbers. */ NormalTSampler(UniformRandomProvider rng) { super(rng); this.sampler = ZigguratSampler.NormalizedGaussian.of(rng); } /** {@inheritDoc} */ @Override public double sample() { return sampler.sample(); } /** {@inheritDoc} */ @Override public NormalTSampler withUniformRandomProvider(UniformRandomProvider rng) { return new NormalTSampler(rng); } } /** * @param rng Generator of uniformly distributed random numbers. */ TSampler(UniformRandomProvider rng) { this.rng = rng; } /** {@inheritDoc} */ // Redeclare the signature to return a TSampler not a SharedStateContinuousSampler @Override public abstract TSampler withUniformRandomProvider(UniformRandomProvider rng); /** * Generates a {@code long} value. * Used by algorithm implementations without exposing access to the RNG. * * @return the next random value */ long nextLong() { return rng.nextLong(); } /** {@inheritDoc} */ @Override public String toString() { return "Student's t deviate [" + rng.toString() + "]"; } /** * Create a new t distribution sampler. * * @param rng Generator of uniformly distributed random numbers. * @param degreesOfFreedom Degrees of freedom. * @return the sampler * @throws IllegalArgumentException if {@code degreesOfFreedom <= 0} */ public static TSampler of(UniformRandomProvider rng, double degreesOfFreedom) { if (degreesOfFreedom > HUGE_DF) { return new NormalTSampler(rng); } else if (degreesOfFreedom > 0) { return new StudentsTSampler(rng, degreesOfFreedom); } else { // df <= 0 or nan throw new IllegalArgumentException( "degrees of freedom is not strictly positive: " + degreesOfFreedom); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy