Skip to content

Commit

Permalink
RNG-52: Conservative upper bound to avoid silent truncation.
Browse files Browse the repository at this point in the history
Functionality is not limited since the Poisson distribution is already
well approximated by a Gaussian for mean values larger than about 1000.
  • Loading branch information
Gilles committed Nov 15, 2018
1 parent 350fb42 commit 8c927dc
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 13 deletions.
Expand Up @@ -38,10 +38,10 @@
*/
public class LargeMeanPoissonSampler
implements DiscreteSampler {

/** Upper bound to avoid truncation. */
private static final double MAX_MEAN = 0.5 * Integer.MAX_VALUE;
/** Class to compute {@code log(n!)}. This has no cached values. */
private static final InternalUtils.FactorialLog NO_CACHE_FACTORIAL_LOG;

/** Used when there is no requirement for a small mean Poisson sampler. */
private static final DiscreteSampler NO_SMALL_MEAN_POISSON_SAMPLER = null;

Expand Down Expand Up @@ -96,19 +96,19 @@ public class LargeMeanPoissonSampler
private final DiscreteSampler smallMeanPoissonSampler;

/**
* @param rng Generator of uniformly distributed random numbers.
* @param rng Generator of uniformly distributed random numbers.
* @param mean Mean.
* @throws IllegalArgumentException if {@code mean <= 0} or
* {@code mean >} {@link Integer#MAX_VALUE}.
* {@code mean > 0.5 *} {@link Integer#MAX_VALUE}.
*/
public LargeMeanPoissonSampler(UniformRandomProvider rng,
double mean) {
if (mean <= 0) {
throw new IllegalArgumentException(mean + " <= " + 0);
}
// The algorithm is not valid if Math.floor(mean) is not an integer.
if (mean > Integer.MAX_VALUE) {
throw new IllegalArgumentException(mean + " > " + Integer.MAX_VALUE);
if (mean > MAX_MEAN) {
throw new IllegalArgumentException(mean + " > " + MAX_MEAN);
}
this.rng = rng;

Expand Down
Expand Up @@ -32,10 +32,12 @@
* @since 1.1
*
* This sampler is suitable for {@code mean < 40}.
* For large means, {@link LargePoissonSampler} should be used instead.
*/
public class SmallMeanPoissonSampler
implements DiscreteSampler {

/** Upper bound to avoid truncation. */
private static final double MAX_MEAN = 0.5 * Integer.MAX_VALUE;
/**
* Pre-compute {@code Math.exp(-mean)}.
* Note: This is the probability of the Poisson sample {@code P(n=0)}.
Expand All @@ -57,6 +59,9 @@ public SmallMeanPoissonSampler(UniformRandomProvider rng,
if (mean <= 0) {
throw new IllegalArgumentException(mean + " <= " + 0);
}
if (mean > MAX_MEAN) {
throw new IllegalArgumentException(mean + " > " + MAX_MEAN);
}

p0 = Math.exp(-mean);
// The returned sample is bounded by 1000 * mean or Integer.MAX_VALUE
Expand Down
Expand Up @@ -35,23 +35,22 @@ public class LargeMeanPoissonSamplerTest {
* Test the constructor with a bad mean.
*/
@Test(expected=IllegalArgumentException.class)
public void testConstructorThrowsWithZeroMean() {
public void testConstructorThrowsWithMeanLargerThanUpperBound() {
final RestorableUniformRandomProvider rng =
RandomSource.create(RandomSource.SPLIT_MIX_64);
@SuppressWarnings("unused")
LargeMeanPoissonSampler sampler = new LargeMeanPoissonSampler(rng, 0);
LargeMeanPoissonSampler sampler = new LargeMeanPoissonSampler(rng, Integer.MAX_VALUE / 2 + 1);
}

/**
* Test the constructor with a mean that is too large.
* Test the constructor with a bad mean.
*/
@Test(expected=IllegalArgumentException.class)
public void testConstructorThrowsWithNonIntegerMean() {
public void testConstructorThrowsWithZeroMean() {
final RestorableUniformRandomProvider rng =
RandomSource.create(RandomSource.SPLIT_MIX_64);
final double mean = Integer.MAX_VALUE + 1.0;
@SuppressWarnings("unused")
LargeMeanPoissonSampler sampler = new LargeMeanPoissonSampler(rng, mean);
LargeMeanPoissonSampler sampler = new LargeMeanPoissonSampler(rng, 0);
}

/**
Expand Down
@@ -0,0 +1,54 @@
/*
* 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.RandomProviderState;
import org.apache.commons.rng.RestorableUniformRandomProvider;
import org.apache.commons.rng.simple.RandomSource;
import org.junit.Assert;
import org.junit.Test;

/**
* This test checks the {@link SmallMeanPoissonSampler} can be created
* from a saved state.
*/
public class SmallMeanPoissonSamplerTest {

// Edge cases for construction

/**
* Test the constructor with a bad mean.
*/
@Test(expected=IllegalArgumentException.class)
public void testConstructorThrowsWithMeanLargerThanUpperBound() {
final RestorableUniformRandomProvider rng =
RandomSource.create(RandomSource.SPLIT_MIX_64);
@SuppressWarnings("unused")
SmallMeanPoissonSampler sampler = new SmallMeanPoissonSampler(rng, Integer.MAX_VALUE / 2 + 1);
}

/**
* Test the constructor with a bad mean.
*/
@Test(expected=IllegalArgumentException.class)
public void testConstructorThrowsWithZeroMean() {
final RestorableUniformRandomProvider rng =
RandomSource.create(RandomSource.SPLIT_MIX_64);
@SuppressWarnings("unused")
SmallMeanPoissonSampler sampler = new SmallMeanPoissonSampler(rng, 0);
}
}
3 changes: 3 additions & 0 deletions src/changes/changes.xml
Expand Up @@ -67,6 +67,9 @@ Additional code is provided in the following module:
It is however not part of the official API and no compatibility
should be expected in subsequent releases.
">
<action dev="erans" type="update" issue="RNG-52">
Set conservative upper bound in "LargePoissonSampler" to avoid truncation.
</action>
<action dev="erans" type="fix" issue="RNG-59">
Use JDK's "SecureRandom" to seed the "SeedFactory".
</action>
Expand Down

0 comments on commit 8c927dc

Please sign in to comment.