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

org.apache.commons.rng.sampling.distribution.FastLoadedDiceRollerDiscreteSampler 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.math.BigInteger;
import java.util.Arrays;
import org.apache.commons.rng.UniformRandomProvider;

/**
 * Distribution sampler that uses the Fast Loaded Dice Roller (FLDR). It can be used to
 * sample from {@code n} values each with an associated relative weight. If all unique items
 * are assigned the same weight it is more efficient to use the {@link DiscreteUniformSampler}.
 *
 * 

Given a list {@code L} of {@code n} positive numbers, * where {@code L[i]} represents the relative weight of the {@code i}th side, FLDR returns * integer {@code i} with relative probability {@code L[i]}. * *

FLDR produces exact samples from the specified probability distribution. *

    *
  • For integer weights, the probability of returning {@code i} is precisely equal to the * rational number {@code L[i] / m}, where {@code m} is the sum of {@code L}. *
  • For floating-points weights, each weight {@code L[i]} is converted to the * corresponding rational number {@code p[i] / q[i]} where {@code p[i]} is a positive integer and * {@code q[i]} is a power of 2. The rational weights are then normalized (exactly) to sum to unity. *
* *

Note that if exact samples are not required then an alternative sampler that * ignores very small relative weights may have improved sampling performance. * *

This implementation is based on the algorithm in: * *

* Feras A. Saad, Cameron E. Freer, Martin C. Rinard, and Vikash K. Mansinghka. * The Fast Loaded Dice Roller: A Near-Optimal Exact Sampler for Discrete Probability * Distributions. In AISTATS 2020: Proceedings of the 23rd International Conference on * Artificial Intelligence and Statistics, Proceedings of Machine Learning Research 108, * Palermo, Sicily, Italy, 2020. *
* *

Sampling uses {@link UniformRandomProvider#nextInt()} as the source of random bits. * * @see Saad et al (2020) * Proceedings of the 23rd International Conference on Artificial Intelligence and Statistics, * PMLR 108:1036-1046. * @since 1.5 */ public abstract class FastLoadedDiceRollerDiscreteSampler implements SharedStateDiscreteSampler { /** * The maximum size of an array. * *

This value is taken from the limit in Open JDK 8 {@code java.util.ArrayList}. * It allows VMs to reserve some header words in an array. */ private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; /** The maximum biased exponent for a finite double. * This is offset by 1023 from {@code Math.getExponent(Double.MAX_VALUE)}. */ private static final int MAX_BIASED_EXPONENT = 2046; /** Size of the mantissa of a double. Equal to 52 bits. */ private static final int MANTISSA_SIZE = 52; /** Mask to extract the 52-bit mantissa from a long representation of a double. */ private static final long MANTISSA_MASK = 0x000f_ffff_ffff_ffffL; /** BigInteger representation of {@link Long#MAX_VALUE}. */ private static final BigInteger MAX_LONG = BigInteger.valueOf(Long.MAX_VALUE); /** The maximum offset that will avoid loss of bits for a left shift of a 53-bit value. * The value will remain positive for any shift {@code <=} this value. */ private static final int MAX_OFFSET = 10; /** Initial value for no leaf node label. */ private static final int NO_LABEL = Integer.MAX_VALUE; /** Name of the sampler. */ private static final String SAMPLER_NAME = "Fast Loaded Dice Roller"; /** * Class to handle the edge case of observations in only one category. */ private static final class FixedValueDiscreteSampler extends FastLoadedDiceRollerDiscreteSampler { /** The sample value. */ private final int sampleValue; /** * @param sampleValue Sample value. */ FixedValueDiscreteSampler(int sampleValue) { this.sampleValue = sampleValue; } @Override public int sample() { return sampleValue; } @Override public FastLoadedDiceRollerDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) { return this; } @Override public String toString() { return SAMPLER_NAME; } } /** * Class to implement the FLDR sample algorithm. */ private static final class FLDRSampler extends FastLoadedDiceRollerDiscreteSampler { /** Empty boolean source. This is the location of the sign-bit after 31 right shifts on * the boolean source. */ private static final int EMPTY_BOOL_SOURCE = 1; /** Underlying source of randomness. */ private final UniformRandomProvider rng; /** Number of categories. */ private final int n; /** Number of levels in the discrete distribution generating (DDG) tree. * Equal to {@code ceil(log2(m))} where {@code m} is the sum of observations. */ private final int k; /** Number of leaf nodes at each level. */ private final int[] h; /** Stores the leaf node labels in increasing order. Named {@code H} in the FLDR paper. */ private final int[] lH; /** * Provides a bit source for booleans. * *

A cached value from a call to {@link UniformRandomProvider#nextInt()}. * *

Only stores 31-bits when full as 1 bit has already been consumed. * The sign bit is a flag that shifts down so the source eventually equals 1 * when all bits are consumed and will trigger a refill. */ private int booleanSource = EMPTY_BOOL_SOURCE; /** * Creates a sampler. * *

The input parameters are not validated and must be correctly computed tables. * * @param rng Generator of uniformly distributed random numbers. * @param n Number of categories * @param k Number of levels in the discrete distribution generating (DDG) tree. * Equal to {@code ceil(log2(m))} where {@code m} is the sum of observations. * @param h Number of leaf nodes at each level. * @param lH Stores the leaf node labels in increasing order. */ FLDRSampler(UniformRandomProvider rng, int n, int k, int[] h, int[] lH) { this.rng = rng; this.n = n; this.k = k; // Deliberate direct storage of input arrays this.h = h; this.lH = lH; } /** * Creates a copy with a new source of randomness. * * @param rng Generator of uniformly distributed random numbers. * @param source Source to copy. */ private FLDRSampler(UniformRandomProvider rng, FLDRSampler source) { this.rng = rng; this.n = source.n; this.k = source.k; this.h = source.h; this.lH = source.lH; } /** {@inheritDoc} */ @Override public int sample() { // ALGORITHM 5: SAMPLE int c = 0; int d = 0; for (;;) { // b = flip() // d = 2 * d + (1 - b) d = (d << 1) + flip(); if (d < h[c]) { // z = H[d][c] final int z = lH[d * k + c]; // assert z != NO_LABEL if (z < n) { return z; } d = 0; c = 0; } else { d = d - h[c]; c++; } } } /** * Provides a source of boolean bits. * *

Note: This replicates the boolean cache functionality of * {@code o.a.c.rng.core.source32.IntProvider}. The method has been simplified to return * an {@code int} value rather than a {@code boolean}. * * @return the bit (0 or 1) */ private int flip() { int bits = booleanSource; if (bits == 1) { // Refill bits = rng.nextInt(); // Store a refill flag in the sign bit and the unused 31 bits, return lowest bit booleanSource = Integer.MIN_VALUE | (bits >>> 1); return bits & 0x1; } // Shift down eventually triggering refill, return current lowest bit booleanSource = bits >>> 1; return bits & 0x1; } /** {@inheritDoc} */ @Override public String toString() { return SAMPLER_NAME + " [" + rng.toString() + "]"; } /** {@inheritDoc} */ @Override public FastLoadedDiceRollerDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) { return new FLDRSampler(rng, this); } } /** Package-private constructor. */ FastLoadedDiceRollerDiscreteSampler() { // Intentionally empty } /** {@inheritDoc} */ // Redeclare the signature to return a FastLoadedDiceRollerSampler not a SharedStateLongSampler @Override public abstract FastLoadedDiceRollerDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng); /** * Creates a sampler. * *

Note: The discrete distribution generating (DDG) tree requires {@code (n + 1) * k} entries * where {@code n} is the number of categories, {@code k == ceil(log2(m))} and {@code m} * is the sum of the observed frequencies. An exception is raised if this cannot be allocated * as a single array. * *

For reference the sum is limited to {@link Long#MAX_VALUE} and the value {@code k} to 63. * The number of categories is limited to approximately {@code ((2^31 - 1) / k) = 34,087,042} * when the sum of frequencies is large enough to create k=63. * * @param rng Generator of uniformly distributed random numbers. * @param frequencies Observed frequencies of the discrete distribution. * @return the sampler * @throws IllegalArgumentException if {@code frequencies} is null or empty, a * frequency is negative, the sum of all frequencies is either zero or * above {@link Long#MAX_VALUE}, or the size of the discrete distribution generating tree * is too large. */ public static FastLoadedDiceRollerDiscreteSampler of(UniformRandomProvider rng, long[] frequencies) { final long m = sum(frequencies); // Obtain indices of non-zero frequencies final int[] indices = indicesOfNonZero(frequencies); // Edge case for 1 non-zero weight. This also handles edge case for 1 observation // (as log2(m) == 0 will break the computation of the DDG tree). if (indices.length == 1) { return new FixedValueDiscreteSampler(indexOfNonZero(frequencies)); } return createSampler(rng, frequencies, indices, m); } /** * Creates a sampler. * *

Weights are converted to rational numbers {@code p / q} where {@code q} is a power of 2. * The numerators {@code p} are scaled to use a common denominator before summing. * *

All weights are used to create the sampler. Weights with a small magnitude relative * to the largest weight can be excluded using the constructor method with the * relative magnitude parameter {@code alpha} (see {@link #of(UniformRandomProvider, double[], int)}). * * @param rng Generator of uniformly distributed random numbers. * @param weights Weights of the discrete distribution. * @return the sampler * @throws IllegalArgumentException if {@code weights} is null or empty, a * weight is negative, infinite or {@code NaN}, the sum of all weights is zero, or the size * of the discrete distribution generating tree is too large. * @see #of(UniformRandomProvider, double[], int) */ public static FastLoadedDiceRollerDiscreteSampler of(UniformRandomProvider rng, double[] weights) { return of(rng, weights, 0); } /** * Creates a sampler. * *

Weights are converted to rational numbers {@code p / q} where {@code q} is * a power of 2. The numerators {@code p} are scaled to use a common * denominator before summing. * *

Note: The discrete distribution generating (DDG) tree requires * {@code (n + 1) * k} entries where {@code n} is the number of categories, * {@code k == ceil(log2(m))} and {@code m} is the sum of the weight numerators * {@code q}. An exception is raised if this cannot be allocated as a single * array. * *

For reference the value {@code k} is equal to or greater than the ratio of * the largest to the smallest weight expressed as a power of 2. For * {@code Double.MAX_VALUE / Double.MIN_VALUE} this is ~2098. The value * {@code k} increases with the sum of the weight numerators. A number of * weights in excess of 1,000,000 with values equal to {@link Double#MAX_VALUE} * would be required to raise an exception when the minimum weight is * {@link Double#MIN_VALUE}. * *

Weights with a small magnitude relative to the largest weight can be * excluded using the relative magnitude parameter {@code alpha}. This will set * any weight to zero if the magnitude is approximately 2alpha * smaller than the largest weight. This comparison is made using only * the exponent of the input weights. The {@code alpha} parameter is ignored if * not above zero. Note that a small {@code alpha} parameter will exclude more * weights than a large {@code alpha} parameter. * *

The alpha parameter can be used to exclude categories that * have a very low probability of occurrence and will improve the construction * performance of the sampler. The effect on sampling performance depends on * the relative weights of the excluded categories; typically a high {@code alpha} * is used to exclude categories that would be visited with a very low probability * and the sampling performance is unchanged. * *

Implementation Note * *

This method creates a sampler with exact samples from the * specified probability distribution. It is recommended to use this method: *

    *
  • if the weights are computed, for example from a probability mass function; or *
  • if the weights sum to an infinite value. *
* *

If the weights are computed from empirical observations then it is * recommended to use the factory method * {@link #of(UniformRandomProvider, long[]) accepting frequencies}. This * requires the total number of observations to be representable as a long * integer. * *

Note that if all weights are scaled by a power of 2 to be integers, and * each integer can be represented as a positive 64-bit long value, then the * sampler created using this method will match the output from a sampler * created with the scaled weights converted to long values for the factory * method {@link #of(UniformRandomProvider, long[]) accepting frequencies}. This * assumes the sum of the integer values does not overflow. * *

It should be noted that the conversion of weights to rational numbers has * a performance overhead during construction (sampling performance is not * affected). This may be avoided by first converting them to integer values * that can be summed without overflow. For example by scaling values by * {@code 2^62 / sum} and converting to long by casting or rounding. * *

This approach may increase the efficiency of construction. The resulting * sampler may no longer produce exact samples from the distribution. * In particular any weights with a converted frequency of zero cannot be * sampled. * * @param rng Generator of uniformly distributed random numbers. * @param weights Weights of the discrete distribution. * @param alpha Alpha parameter. * @return the sampler * @throws IllegalArgumentException if {@code weights} is null or empty, a * weight is negative, infinite or {@code NaN}, the sum of all weights is zero, * or the size of the discrete distribution generating tree is too large. * @see #of(UniformRandomProvider, long[]) */ public static FastLoadedDiceRollerDiscreteSampler of(UniformRandomProvider rng, double[] weights, int alpha) { final int n = checkWeightsNonZeroLength(weights); // Convert floating-point double to a relative weight // using a shifted integer representation final long[] frequencies = new long[n]; final int[] offsets = new int[n]; convertToIntegers(weights, frequencies, offsets, alpha); // Obtain indices of non-zero weights final int[] indices = indicesOfNonZero(frequencies); // Edge case for 1 non-zero weight. if (indices.length == 1) { return new FixedValueDiscreteSampler(indexOfNonZero(frequencies)); } final BigInteger m = sum(frequencies, offsets, indices); // Use long arithmetic if possible. This occurs when the weights are similar in magnitude. if (m.compareTo(MAX_LONG) <= 0) { // Apply the offset for (int i = 0; i < n; i++) { frequencies[i] <<= offsets[i]; } return createSampler(rng, frequencies, indices, m.longValue()); } return createSampler(rng, frequencies, offsets, indices, m); } /** * Sum the frequencies. * * @param frequencies Frequencies. * @return the sum * @throws IllegalArgumentException if {@code frequencies} is null or empty, a * frequency is negative, or the sum of all frequencies is either zero or above * {@link Long#MAX_VALUE} */ private static long sum(long[] frequencies) { // Validate if (frequencies == null || frequencies.length == 0) { throw new IllegalArgumentException("frequencies must contain at least 1 value"); } // Sum the values. // Combine all the sign bits in the observations and the intermediate sum in a flag. long m = 0; long signFlag = 0; for (final long o : frequencies) { m += o; signFlag |= o | m; } // Check for a sign-bit. if (signFlag < 0) { // One or more observations were negative, or the sum overflowed. for (final long o : frequencies) { if (o < 0) { throw new IllegalArgumentException("frequencies must contain positive values: " + o); } } throw new IllegalArgumentException("Overflow when summing frequencies"); } if (m == 0) { throw new IllegalArgumentException("Sum of frequencies is zero"); } return m; } /** * Convert the floating-point weights to relative weights represented as * integers {@code value * 2^exponent}. The relative weight as an integer is: * *

     * BigInteger.valueOf(value).shiftLeft(exponent)
     * 
* *

Note that the weights are created using a common power-of-2 scaling * operation so the minimum exponent is zero. * *

A positive {@code alpha} parameter is used to set any weight to zero if * the magnitude is approximately 2alpha smaller than the * largest weight. This comparison is made using only the exponent of the input * weights. * * @param weights Weights of the discrete distribution. * @param values Output floating-point mantissas converted to 53-bit integers. * @param exponents Output power of 2 exponent. * @param alpha Alpha parameter. * @throws IllegalArgumentException if a weight is negative, infinite or * {@code NaN}, or the sum of all weights is zero. */ private static void convertToIntegers(double[] weights, long[] values, int[] exponents, int alpha) { int maxExponent = Integer.MIN_VALUE; for (int i = 0; i < weights.length; i++) { final double weight = weights[i]; // Ignore zero. // When creating the integer value later using bit shifts the result will remain zero. if (weight == 0) { continue; } final long bits = Double.doubleToRawLongBits(weight); // For the IEEE 754 format see Double.longBitsToDouble(long). // Extract the exponent (with the sign bit) int exp = (int) (bits >>> MANTISSA_SIZE); // Detect negative, infinite or NaN. // Note: Negative values sign bit will cause the exponent to be too high. if (exp > MAX_BIASED_EXPONENT) { throw new IllegalArgumentException("Invalid weight: " + weight); } long mantissa; if (exp == 0) { // Sub-normal number: mantissa = (bits & MANTISSA_MASK) << 1; // Here we convert to a normalised number by counting the leading zeros // to obtain the number of shifts of the most significant bit in // the mantissa that is required to get a 1 at position 53 (i.e. as // if it were a normal number with assumed leading bit). final int shift = Long.numberOfLeadingZeros(mantissa << 11); mantissa <<= shift; exp -= shift; } else { // Normal number. Add the implicit leading 1-bit. mantissa = (bits & MANTISSA_MASK) | (1L << MANTISSA_SIZE); } // Here the floating-point number is equal to: // mantissa * 2^(exp-1075) values[i] = mantissa; exponents[i] = exp; maxExponent = Math.max(maxExponent, exp); } // No exponent indicates that all weights are zero if (maxExponent == Integer.MIN_VALUE) { throw new IllegalArgumentException("Sum of weights is zero"); } filterWeights(values, exponents, alpha, maxExponent); scaleWeights(values, exponents); } /** * Filters small weights using the {@code alpha} parameter. * A positive {@code alpha} parameter is used to set any weight to zero if * the magnitude is approximately 2alpha smaller than the * largest weight. This comparison is made using only the exponent of the input * weights. * * @param values 53-bit values. * @param exponents Power of 2 exponent. * @param alpha Alpha parameter. * @param maxExponent Maximum exponent. */ private static void filterWeights(long[] values, int[] exponents, int alpha, int maxExponent) { if (alpha > 0) { // Filter weights. This must be done before the values are shifted so // the exponent represents the approximate magnitude of the value. for (int i = 0; i < exponents.length; i++) { if (maxExponent - exponents[i] > alpha) { values[i] = 0; } } } } /** * Scale the weights represented as integers {@code value * 2^exponent} to use a * minimum exponent of zero. The values are scaled to remove any common trailing zeros * in their representation. This ultimately reduces the size of the discrete distribution * generating (DGG) tree. * * @param values 53-bit values. * @param exponents Power of 2 exponent. */ private static void scaleWeights(long[] values, int[] exponents) { // Find the minimum exponent and common trailing zeros. int minExponent = Integer.MAX_VALUE; for (int i = 0; i < exponents.length; i++) { if (values[i] != 0) { minExponent = Math.min(minExponent, exponents[i]); } } // Trailing zeros occur when the original weights have a representation with // less than 52 binary digits, e.g. {1.5, 0.5, 0.25}. int trailingZeros = Long.SIZE; for (int i = 0; i < values.length && trailingZeros != 0; i++) { trailingZeros = Math.min(trailingZeros, Long.numberOfTrailingZeros(values[i])); } // Scale by a power of 2 so the minimum exponent is zero. for (int i = 0; i < exponents.length; i++) { exponents[i] -= minExponent; } // Remove common trailing zeros. if (trailingZeros != 0) { for (int i = 0; i < values.length; i++) { values[i] >>>= trailingZeros; } } } /** * Sum the integers at the specified indices. * Integers are represented as {@code value * 2^exponent}. * * @param values 53-bit values. * @param exponents Power of 2 exponent. * @param indices Indices to sum. * @return the sum */ private static BigInteger sum(long[] values, int[] exponents, int[] indices) { BigInteger m = BigInteger.ZERO; for (final int i : indices) { m = m.add(toBigInteger(values[i], exponents[i])); } return m; } /** * Convert the value and left shift offset to a BigInteger. * It is assumed the value is at most 53-bits. This allows optimising the left * shift if it is below 11 bits. * * @param value 53-bit value. * @param offset Left shift offset (must be positive). * @return the BigInteger */ private static BigInteger toBigInteger(long value, int offset) { // Ignore zeros. The sum method uses indices of non-zero values. if (offset <= MAX_OFFSET) { // Assume (value << offset) <= Long.MAX_VALUE return BigInteger.valueOf(value << offset); } return BigInteger.valueOf(value).shiftLeft(offset); } /** * Creates the sampler. * *

It is assumed the frequencies are all positive and the sum does not * overflow. * * @param rng Generator of uniformly distributed random numbers. * @param frequencies Observed frequencies of the discrete distribution. * @param indices Indices of non-zero frequencies. * @param m Sum of the frequencies. * @return the sampler */ private static FastLoadedDiceRollerDiscreteSampler createSampler(UniformRandomProvider rng, long[] frequencies, int[] indices, long m) { // ALGORITHM 5: PREPROCESS // a == frequencies // m = sum(a) // h = leaf node count // H = leaf node label (lH) final int n = frequencies.length; // k = ceil(log2(m)) final int k = 64 - Long.numberOfLeadingZeros(m - 1); // r = a(n+1) = 2^k - m final long r = (1L << k) - m; // Note: // A sparse matrix can often be used for H, as most of its entries are empty. // This implementation uses a 1D array for efficiency at the cost of memory. // This is limited to approximately ((2^31 - 1) / k), e.g. 34087042 when the sum of // observations is large enough to create k=63. // This could be handled using a 2D array. In practice a number of categories this // large is not expected and is currently not supported. final int[] h = new int[k]; final int[] lH = new int[checkArraySize((n + 1L) * k)]; Arrays.fill(lH, NO_LABEL); int d; for (int j = 0; j < k; j++) { final int shift = (k - 1) - j; final long bitMask = 1L << shift; d = 0; for (final int i : indices) { // bool w ← (a[i] >> (k − 1) − j)) & 1 // h[j] = h[j] + w // if w then: if ((frequencies[i] & bitMask) != 0) { h[j]++; // H[d][j] = i lH[d * k + j] = i; d++; } } // process a(n+1) without extending the input frequencies array by 1 if ((r & bitMask) != 0) { h[j]++; lH[d * k + j] = n; } } return new FLDRSampler(rng, n, k, h, lH); } /** * Creates the sampler. Frequencies are represented as a 53-bit value with a * left-shift offset. *

     * BigInteger.valueOf(value).shiftLeft(offset)
     * 
* *

It is assumed the frequencies are all positive. * * @param rng Generator of uniformly distributed random numbers. * @param frequencies Observed frequencies of the discrete distribution. * @param offsets Left shift offsets (must be positive). * @param indices Indices of non-zero frequencies. * @param m Sum of the frequencies. * @return the sampler */ private static FastLoadedDiceRollerDiscreteSampler createSampler(UniformRandomProvider rng, long[] frequencies, int[] offsets, int[] indices, BigInteger m) { // Repeat the logic from createSampler(...) using extended arithmetic to test the bits // ALGORITHM 5: PREPROCESS // a == frequencies // m = sum(a) // h = leaf node count // H = leaf node label (lH) final int n = frequencies.length; // k = ceil(log2(m)) final int k = m.subtract(BigInteger.ONE).bitLength(); // r = a(n+1) = 2^k - m final BigInteger r = BigInteger.ONE.shiftLeft(k).subtract(m); final int[] h = new int[k]; final int[] lH = new int[checkArraySize((n + 1L) * k)]; Arrays.fill(lH, NO_LABEL); int d; for (int j = 0; j < k; j++) { final int shift = (k - 1) - j; d = 0; for (final int i : indices) { // bool w ← (a[i] >> (k − 1) − j)) & 1 // h[j] = h[j] + w // if w then: if (testBit(frequencies[i], offsets[i], shift)) { h[j]++; // H[d][j] = i lH[d * k + j] = i; d++; } } // process a(n+1) without extending the input frequencies array by 1 if (r.testBit(shift)) { h[j]++; lH[d * k + j] = n; } } return new FLDRSampler(rng, n, k, h, lH); } /** * Test the logical bit of the shifted integer representation. * The value is assumed to have at most 53-bits of information. The offset * is assumed to be positive. This is functionally equivalent to: *

     * BigInteger.valueOf(value).shiftLeft(offset).testBit(n)
     * 
* * @param value 53-bit value. * @param offset Left shift offset. * @param n Index of bit to test. * @return true if the bit is 1 */ private static boolean testBit(long value, int offset, int n) { if (n < offset) { // All logical trailing bits are zero return false; } // Test if outside the 53-bit value (note that the implicit 1 bit // has been added to the 52-bit mantissas for 'normal' floating-point numbers). final int bit = n - offset; return bit <= MANTISSA_SIZE && (value & (1L << bit)) != 0; } /** * Check the weights have a non-zero length. * * @param weights Weights. * @return the length */ private static int checkWeightsNonZeroLength(double[] weights) { if (weights == null || weights.length == 0) { throw new IllegalArgumentException("weights must contain at least 1 value"); } return weights.length; } /** * Create the indices of non-zero values. * * @param values Values. * @return the indices */ private static int[] indicesOfNonZero(long[] values) { int n = 0; final int[] indices = new int[values.length]; for (int i = 0; i < values.length; i++) { if (values[i] != 0) { indices[n++] = i; } } return Arrays.copyOf(indices, n); } /** * Find the index of the first non-zero frequency. * * @param frequencies Frequencies. * @return the index * @throws IllegalStateException if all frequencies are zero. */ static int indexOfNonZero(long[] frequencies) { for (int i = 0; i < frequencies.length; i++) { if (frequencies[i] != 0) { return i; } } throw new IllegalStateException("All frequencies are zero"); } /** * Check the size is valid for a 1D array. * * @param size Size * @return the size as an {@code int} * @throws IllegalArgumentException if the size is too large for a 1D array. */ static int checkArraySize(long size) { if (size > MAX_ARRAY_SIZE) { throw new IllegalArgumentException("Unable to allocate array of size: " + size); } return (int) size; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy