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-rng.git
The following commit(s) were added to refs/heads/master by this push: new a2e5e79 RNG-148: Revert detection of infinite length vectors a2e5e79 is described below commit a2e5e7937a1ea62f38efa8f270aac378b8286ccf Author: aherbert <aherb...@apache.org> AuthorDate: Fri Jul 9 11:08:12 2021 +0100 RNG-148: Revert detection of infinite length vectors Due to the change in RNG-154 the underlying Gaussian cannot have infinite tails. --- .../commons/rng/sampling/UnitSphereSampler.java | 33 ++------- .../rng/sampling/UnitSphereSamplerTest.java | 78 ++++++++-------------- src/changes/changes.xml | 3 - 3 files changed, 32 insertions(+), 82 deletions(-) diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/UnitSphereSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/UnitSphereSampler.java index e9b2a2a..76db7b0 100644 --- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/UnitSphereSampler.java +++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/UnitSphereSampler.java @@ -104,8 +104,8 @@ public class UnitSphereSampler implements SharedStateObjectSampler<double[]> { final double y = sampler.sample(); final double sum = x * x + y * y; - if (isInvalidSumForNormalization(sum)) { - // Invalid vector is discarded. + if (sum == 0) { + // Zero-norm vector is discarded. return sample(); } @@ -141,8 +141,8 @@ public class UnitSphereSampler implements SharedStateObjectSampler<double[]> { final double z = sampler.sample(); final double sum = x * x + y * y + z * z; - if (isInvalidSumForNormalization(sum)) { - // Invalid vector is discarded. + if (sum == 0) { + // Zero-norm vector is discarded. return sample(); } @@ -187,8 +187,8 @@ public class UnitSphereSampler implements SharedStateObjectSampler<double[]> { sum += x * x; } - if (isInvalidSumForNormalization(sum)) { - // Invalid vector is discarded. + if (sum == 0) { + // Zero-norm vector is discarded. // Using recursion as it is highly unlikely to generate more // than a few such vectors. It also protects against infinite // loop (in case a buggy generator is used), by eventually @@ -284,25 +284,4 @@ public class UnitSphereSampler implements SharedStateObjectSampler<double[]> { } return new UnitSphereSamplerND(dimension, rng); } - - /** - * Returns true if the sum of squared components of a vector is invalid for - * normalization. - * - * <p>This is true for any sum where the factor {@code f = 1.0 / sqrt(sum)} - * cannot be used to create a unit length vector by multiplication. The sum - * is invalid if: - * - * <ul> - * <li>{@code sum = 0} then {@code f = infinity} - * <li>{@code sum = infinity} then {@code f = 0} - * </ul> - * - * @param sum Sum of squared components of a vector - * @return true if invalid for normalisation - */ - private static boolean isInvalidSumForNormalization(double sum) { - // Note: Deliberate floating-point comparison with zero - return sum == 0 || sum == Double.POSITIVE_INFINITY; - } } diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/UnitSphereSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/UnitSphereSamplerTest.java index 9733858..005d548 100644 --- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/UnitSphereSamplerTest.java +++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/UnitSphereSamplerTest.java @@ -388,72 +388,46 @@ public class UnitSphereSamplerTest { } /** - * Test the edge case where the normalisation sum to divide by is zero. + * Test the edge case where the normalisation sum to divide by is zero for 2D. */ @Test - public void testInvalidInverseNormalisationWithZeroLength() { - for (int dim = 2; dim <= 4; dim++) { - testInvalidInverseNormalisationND(dim, true); - } + public void testInvalidInverseNormalisation2D() { + testInvalidInverseNormalisationND(2); } /** - * Test the edge case where the normalisation sum to divide by is infinite. + * Test the edge case where the normalisation sum to divide by is zero for 3D. */ @Test - public void testInvalidInverseNormalisationWithInfiniteLength() { - for (int dim = 2; dim <= 4; dim++) { - testInvalidInverseNormalisationND(dim, false); - } + public void testInvalidInverseNormalisation3D() { + testInvalidInverseNormalisationND(3); } /** - * Test the edge case where the normalisation sum to divide by is zero or infinite. This - * test requires generation of Gaussian samples with the value 0 or infinity. See RNG-55. - * - * @param dimension the dimension - * @param zeroSum true if the sum for the first vector should be zero + * Test the edge case where the normalisation sum to divide by is zero for 4D. + */ + @Test + public void testInvalidInverseNormalisation4D() { + testInvalidInverseNormalisationND(4); + } + + /** + * Test the edge case where the normalisation sum to divide by is zero. + * This test requires generation of Gaussian samples with the value 0. + * See RNG-55. */ - private static void testInvalidInverseNormalisationND(final int dimension, boolean zeroSum) { + private static void testInvalidInverseNormalisationND(final int dimension) { // Create a provider that will create a bad first sample but then recover. // This checks recursion will return a good value. + final UniformRandomProvider bad = new SplitMix64(0x1a2b3cL) { + private int count = -2 * dimension; - // This sampler will createbvalues that manipulate the underlying Gaussian sampler. - UniformRandomProvider bad; - if (zeroSum) { - // Create Gaussian samples of zero - bad = new SplitMix64(0x1a2b3cL) { - private int count = -2 * dimension; - - @Override - public long nextLong() { - // Return enough zeros to create Gaussian samples of zero for all coordinates. - return count++ < 0 ? 0 : super.nextLong(); - } - }; - } else { - // Create a Gaussian sample of infinity. - // This only requires 1 infinite value to create an infinite length vector. - // Assumes the ZigguratNormalizedGaussianSampler. - // To create infinity requires a very large long value with the lowest 7 bits as 0, - // then two doubles of zero. - bad = new SplitMix64(0x1a2b3cL) { - private int lcount = -1; - private int dcount = -2; - - @Override - public long nextLong() { - return lcount++ < 0 ? - (-1L << 7) & Long.MAX_VALUE : - super.nextLong(); - } - - @Override - public double nextDouble() { - return dcount++ < 0 ? 0 : super.nextDouble(); - } - }; - } + @Override + public long nextLong() { + // Return enough zeros to create Gaussian samples of zero for all coordinates. + return count++ < 0 ? 0 : super.nextLong(); + } + }; final double[] vector = UnitSphereSampler.of(dimension, bad).sample(); Assert.assertEquals(dimension, vector.length); diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 77c87dd..e211d01 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -94,9 +94,6 @@ as 'flaky' in the report). New "ZigguratSampler" implementation of the modified "Ziggurat" algorithm for Gaussian and exponential sampling. </action> - <action dev="aherbert" type="fix" issue="148"> - "UnitSphereSampler": Detect and discard infinite length vectors. - </action> <action dev="aherbert" type="add" issue="147"> New "LevySampler" to sample from a Levy distribution. </action>