You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by ah...@apache.org on 2019/07/31 21:47:23 UTC
[commons-rng] 05/08: RNG-110: Provide factory constructors for
unreleased samplers.
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
commit b3a439bab6a71828b9d3426b37c895492de9a2ca
Author: Alex Herbert <ah...@apache.org>
AuthorDate: Fri Jul 19 22:08:50 2019 +0100
RNG-110: Provide factory constructors for unreleased samplers.
These samplers have no requirement to maintain an instance constructor.
The constructor has been made private.
---
.../AliasMethodDiscreteSamplerPerformance.java | 4 +-
.../distribution/DiscreteSamplersPerformance.java | 12 +-
.../distribution/GeometricSamplersPerformance.java | 2 +-
.../distribution/PoissonSamplersPerformance.java | 2 +-
.../distribution/AliasMethodDiscreteSampler.java | 18 +-
.../sampling/distribution/GeometricSampler.java | 52 +-
.../distribution/GuideTableDiscreteSampler.java | 158 +--
.../distribution/KempSmallMeanPoissonSampler.java | 62 +-
.../distribution/LargeMeanPoissonSampler.java | 4 +-
.../MarsagliaTsangWangDiscreteSampler.java | 1063 ++++++++++----------
.../AliasMethodDiscreteSamplerTest.java | 14 +-
.../distribution/DiscreteSamplersList.java | 20 +-
.../distribution/GeometricSamplerTest.java | 18 +-
.../GuideTableDiscreteSamplerTest.java | 12 +-
.../KempSmallMeanPoissonSamplerTest.java | 16 +-
.../MarsagliaTsangWangDiscreteSamplerTest.java | 58 +-
src/main/resources/pmd/pmd-ruleset.xml | 3 +-
17 files changed, 778 insertions(+), 740 deletions(-)
diff --git a/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/distribution/AliasMethodDiscreteSamplerPerformance.java b/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/distribution/AliasMethodDiscreteSamplerPerformance.java
index 408e1ba..32d8307 100644
--- a/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/distribution/AliasMethodDiscreteSamplerPerformance.java
+++ b/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/distribution/AliasMethodDiscreteSamplerPerformance.java
@@ -96,7 +96,7 @@ public class AliasMethodDiscreteSamplerPerformance {
public void setup() {
probabilities = createProbabilities(size);
UniformRandomProvider rng = RandomSource.create(RandomSource.SPLIT_MIX_64);
- sampler = AliasMethodDiscreteSampler.create(rng, probabilities, alpha);
+ sampler = AliasMethodDiscreteSampler.of(rng, probabilities, alpha);
}
/**
@@ -155,6 +155,6 @@ public class AliasMethodDiscreteSamplerPerformance {
@Benchmark
public Object createSampler(DistributionData dist) {
// For the construction the RNG can be null
- return AliasMethodDiscreteSampler.create(null, dist.getProbabilities(), dist.getAlpha());
+ return AliasMethodDiscreteSampler.of(null, dist.getProbabilities(), dist.getAlpha());
}
}
diff --git a/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/distribution/DiscreteSamplersPerformance.java b/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/distribution/DiscreteSamplersPerformance.java
index c8cb81d..03bae18 100644
--- a/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/distribution/DiscreteSamplersPerformance.java
+++ b/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/distribution/DiscreteSamplersPerformance.java
@@ -119,17 +119,17 @@ public class DiscreteSamplersPerformance {
// Note: Use with a fractional part to the mean includes a small mean sample
sampler = new LargeMeanPoissonSampler(rng, 41.7);
} else if ("GeometricSampler".equals(samplerType)) {
- sampler = new GeometricSampler(rng, 0.21);
+ sampler = GeometricSampler.of(rng, 0.21);
} else if ("MarsagliaTsangWangDiscreteSampler".equals(samplerType)) {
- sampler = MarsagliaTsangWangDiscreteSampler.createDiscreteDistribution(rng, DISCRETE_PROBABILITIES);
+ sampler = MarsagliaTsangWangDiscreteSampler.Enumerated.of(rng, DISCRETE_PROBABILITIES);
} else if ("MarsagliaTsangWangPoissonSampler".equals(samplerType)) {
- sampler = MarsagliaTsangWangDiscreteSampler.createPoissonDistribution(rng, 8.9);
+ sampler = MarsagliaTsangWangDiscreteSampler.Poisson.of(rng, 8.9);
} else if ("MarsagliaTsangWangBinomialSampler".equals(samplerType)) {
- sampler = MarsagliaTsangWangDiscreteSampler.createBinomialDistribution(rng, 20, 0.33);
+ sampler = MarsagliaTsangWangDiscreteSampler.Binomial.of(rng, 20, 0.33);
} else if ("GuideTableDiscreteSampler".equals(samplerType)) {
- sampler = new GuideTableDiscreteSampler(rng, DISCRETE_PROBABILITIES);
+ sampler = GuideTableDiscreteSampler.of(rng, DISCRETE_PROBABILITIES);
} else if ("AliasMethodDiscreteSampler".equals(samplerType)) {
- sampler = AliasMethodDiscreteSampler.create(rng, DISCRETE_PROBABILITIES);
+ sampler = AliasMethodDiscreteSampler.of(rng, DISCRETE_PROBABILITIES);
}
}
}
diff --git a/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/distribution/GeometricSamplersPerformance.java b/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/distribution/GeometricSamplersPerformance.java
index b87ef8f..7fc3e57 100644
--- a/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/distribution/GeometricSamplersPerformance.java
+++ b/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/distribution/GeometricSamplersPerformance.java
@@ -95,7 +95,7 @@ public class GeometricSamplersPerformance {
final RandomSource randomSource = RandomSource.valueOf(randomSourceName);
final UniformRandomProvider rng = RandomSource.create(randomSource);
if ("GeometricSampler".equals(samplerType)) {
- sampler = new GeometricSampler(rng, probabilityOfSuccess);
+ sampler = GeometricSampler.of(rng, probabilityOfSuccess);
} else {
final DiscreteInverseCumulativeProbabilityFunction geometricFunction =
new GeometricDiscreteInverseCumulativeProbabilityFunction(probabilityOfSuccess);
diff --git a/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/distribution/PoissonSamplersPerformance.java b/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/distribution/PoissonSamplersPerformance.java
index fbce71e..2ef6f8b 100644
--- a/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/distribution/PoissonSamplersPerformance.java
+++ b/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/distribution/PoissonSamplersPerformance.java
@@ -185,7 +185,7 @@ public class PoissonSamplersPerformance {
factory = new DiscreteSamplerFactory() {
@Override
public DiscreteSampler create() {
- return new KempSmallMeanPoissonSampler(generator, mean);
+ return KempSmallMeanPoissonSampler.of(generator, mean);
}
};
} else if ("BoundedKempSmallMeanPoissonSampler".equals(samplerType)) {
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/AliasMethodDiscreteSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/AliasMethodDiscreteSampler.java
index 2ad3593..2fa1ef7 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/AliasMethodDiscreteSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/AliasMethodDiscreteSampler.java
@@ -52,7 +52,7 @@ import java.util.Arrays;
* approximately 12 bytes of storage per input probability, that is {@code n * 12} for size
* {@code n}. Zero-padding only requires 4 bytes of storage per padded value as the probability is
* known to be zero. A table can be padded to a power of 2 using the utility function
- * {@link #create(UniformRandomProvider, double[], int)} to construct the sampler.</p>
+ * {@link #of(UniformRandomProvider, double[], int)} to construct the sampler.</p>
*
* <p>An optimisation is performed for small table sizes that are a power of 2. In this case the
* sampling uses 1 or 2 calls from {@link UniformRandomProvider#nextInt()} to generate up to
@@ -293,7 +293,7 @@ public class AliasMethodDiscreteSampler
* power-of-two. Padding is bounded by the upper limit on the size of an array.</p>
*
* <p>To avoid zero-padding use the
- * {@link #create(UniformRandomProvider, double[], int)} method with a negative
+ * {@link #of(UniformRandomProvider, double[], int)} method with a negative
* {@code alpha} factor.</p>
*
* @param rng Generator of uniformly distributed random numbers.
@@ -302,11 +302,11 @@ public class AliasMethodDiscreteSampler
* @throws IllegalArgumentException if {@code probabilities} is null or empty, a
* probability is negative, infinite or {@code NaN}, or the sum of all
* probabilities is not strictly positive.
- * @see #create(UniformRandomProvider, double[], int)
+ * @see #of(UniformRandomProvider, double[], int)
*/
- public static AliasMethodDiscreteSampler create(final UniformRandomProvider rng,
- final double[] probabilities) {
- return create(rng, probabilities, DEFAULT_ALPHA);
+ public static SharedStateDiscreteSampler of(final UniformRandomProvider rng,
+ final double[] probabilities) {
+ return of(rng, probabilities, DEFAULT_ALPHA);
}
/**
@@ -348,9 +348,9 @@ public class AliasMethodDiscreteSampler
* probability is negative, infinite or {@code NaN}, or the sum of all
* probabilities is not strictly positive.
*/
- public static AliasMethodDiscreteSampler create(final UniformRandomProvider rng,
- final double[] probabilities,
- int alpha) {
+ public static SharedStateDiscreteSampler of(final UniformRandomProvider rng,
+ final double[] probabilities,
+ int alpha) {
// The Alias method balances N categories with counts around the mean into N sections,
// each allocated 'mean' observations.
//
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/GeometricSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/GeometricSampler.java
index 25a4703..7cbca6b 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/GeometricSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/GeometricSampler.java
@@ -45,10 +45,7 @@ import org.apache.commons.rng.UniformRandomProvider;
*
* @since 1.3
*/
-public class GeometricSampler implements SharedStateDiscreteSampler {
- /** The appropriate geometric sampler for the parameters. */
- private final SharedStateDiscreteSampler delegate;
-
+public final class GeometricSampler {
/**
* Sample from the geometric distribution when the probability of success is 1.
*/
@@ -132,48 +129,29 @@ public class GeometricSampler implements SharedStateDiscreteSampler {
}
}
+ /** Class contains only static methods. */
+ private GeometricSampler() {}
+
/**
- * Creates a new geometric distribution sampler. The samples will be provided in the set
- * {@code k=[0, 1, 2, ...]} where {@code k} indicates the number of failures before the first
- * success.
+ * Creates a new geometric distribution sampler. The samples will be provided in
+ * the set {@code k=[0, 1, 2, ...]} where {@code k} indicates the number of
+ * failures before the first success.
*
- * @param rng Generator of uniformly distributed random numbers
- * @param probabilityOfSuccess The probability of success
- * @throws IllegalArgumentException if {@code probabilityOfSuccess} is not in the range
- * {@code [0 < probabilityOfSuccess <= 1]})
+ * @param rng Generator of uniformly distributed random numbers.
+ * @param probabilityOfSuccess The probability of success.
+ * @return the sampler
+ * @throws IllegalArgumentException if {@code probabilityOfSuccess} is not in
+ * the range {@code [0 < probabilityOfSuccess <= 1]})
*/
- public GeometricSampler(UniformRandomProvider rng, double probabilityOfSuccess) {
+ public static SharedStateDiscreteSampler of(UniformRandomProvider rng,
+ double probabilityOfSuccess) {
if (probabilityOfSuccess <= 0 || probabilityOfSuccess > 1) {
throw new IllegalArgumentException(
"Probability of success (p) must be in the range [0 < p <= 1]: " +
probabilityOfSuccess);
}
- delegate = probabilityOfSuccess == 1 ?
+ return probabilityOfSuccess == 1 ?
GeometricP1Sampler.INSTANCE :
new GeometricExponentialSampler(rng, probabilityOfSuccess);
}
-
- /**
- * Create a sample from a geometric distribution.
- *
- * <p>The sample will take the values in the set {@code [0, 1, 2, ...]}, equivalent to the
- * number of failures before the first success.
- */
- @Override
- public int sample() {
- return delegate.sample();
- }
-
- /** {@inheritDoc} */
- @Override
- public String toString() {
- return delegate.toString();
- }
-
- /** {@inheritDoc} */
- @Override
- public SharedStateDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
- // Direct return of the optimised sampler
- return delegate.withUniformRandomProvider(rng);
- }
}
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/GuideTableDiscreteSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/GuideTableDiscreteSampler.java
index 0ca3315..3ad4218 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/GuideTableDiscreteSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/GuideTableDiscreteSampler.java
@@ -19,27 +19,27 @@ package org.apache.commons.rng.sampling.distribution;
import org.apache.commons.rng.UniformRandomProvider;
/**
- * Compute a sample from a discrete probability distribution. The cumulative probability
- * distribution is searched using a guide table to set an initial start point. This implementation
- * is based on:
+ * Compute a sample from {@code n} values each with an associated probability. If all unique items
+ * are assigned the same probability it is more efficient to use the {@link DiscreteUniformSampler}.
*
- * <ul>
- * <li>
- * <blockquote>
- * Devroye, Luc (1986). Non-Uniform Random Variate Generation.
- * New York: Springer-Verlag. Chapter 3.2.4 "The method of guide tables" p. 96.
- * </blockquote>
- * </li>
- * </ul>
+ * <p>The cumulative probability distribution is searched using a guide table to set an
+ * initial start point. This implementation is based on:</p>
+ *
+ * <blockquote>
+ * Devroye, Luc (1986). Non-Uniform Random Variate Generation.
+ * New York: Springer-Verlag. Chapter 3.2.4 "The method of guide tables" p. 96.
+ * </blockquote>
*
* <p>The size of the guide table can be controlled using a parameter. A larger guide table
* will improve performance at the cost of storage space.</p>
*
* <p>Sampling uses {@link UniformRandomProvider#nextDouble()}.</p>
*
+ * @see <a href="http://en.wikipedia.org/wiki/Probability_distribution#Discrete_probability_distribution">
+ * Discrete probability distribution (Wikipedia)</a>
* @since 1.3
*/
-public class GuideTableDiscreteSampler
+public final class GuideTableDiscreteSampler
implements SharedStateDiscreteSampler {
/** The default value for {@code alpha}. */
private static final double DEFAULT_ALPHA = 1.0;
@@ -63,38 +63,93 @@ public class GuideTableDiscreteSampler
private final int[] guideTable;
/**
- * Create a new instance using the default guide table size.
+ * @param rng Generator of uniformly distributed random numbers.
+ * @param cumulativeProbabilities The cumulative probability table ({@code f(x)}).
+ * @param guideTable The inverse cumulative probability guide table.
+ */
+ private GuideTableDiscreteSampler(UniformRandomProvider rng,
+ double[] cumulativeProbabilities,
+ int[] guideTable) {
+ this.rng = rng;
+ this.cumulativeProbabilities = cumulativeProbabilities;
+ this.guideTable = guideTable;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int sample() {
+ // Compute a probability
+ final double u = rng.nextDouble();
+
+ // Initialise the search using the guide table to find an initial guess.
+ // The table provides an upper bound on the sample (x+1) for a known
+ // cumulative probability (f(x)).
+ int x = guideTable[getGuideTableIndex(u, guideTable.length)];
+ // Search down.
+ // In the edge case where u is 1.0 then 'x' will be 1 outside the range of the
+ // cumulative probability table and this will decrement to a valid range.
+ // In the case where 'u' is mapped to the same guide table index as a lower
+ // cumulative probability f(x) (due to rounding down) then this will not decrement
+ // and return the exclusive upper bound (x+1).
+ while (x != 0 && u <= cumulativeProbabilities[x - 1]) {
+ x--;
+ }
+ return x;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ return "Guide table deviate [" + rng.toString() + "]";
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public SharedStateDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
+ return new GuideTableDiscreteSampler(rng, cumulativeProbabilities, guideTable);
+ }
+
+ /**
+ * Create a new sampler for an enumerated distribution using the given {@code probabilities}.
+ * The samples corresponding to each probability are assumed to be a natural sequence
+ * starting at zero.
+ *
+ * <p>The size of the guide table is {@code probabilities.length}.</p>
*
* @param rng Generator of uniformly distributed random numbers.
* @param probabilities The probabilities.
+ * @return the sampler
* @throws IllegalArgumentException if {@code probabilities} is null or empty, a
* probability is negative, infinite or {@code NaN}, or the sum of all
* probabilities is not strictly positive.
*/
- public GuideTableDiscreteSampler(UniformRandomProvider rng,
- double[] probabilities) {
- this(rng, probabilities, DEFAULT_ALPHA);
+ public static SharedStateDiscreteSampler of(UniformRandomProvider rng,
+ double[] probabilities) {
+ return of(rng, probabilities, DEFAULT_ALPHA);
}
/**
- * Create a new instance.
+ * Create a new sampler for an enumerated distribution using the given {@code probabilities}.
+ * The samples corresponding to each probability are assumed to be a natural sequence
+ * starting at zero.
*
- * <p>The size of the guide table is {@code alpha * probabilities.length}.
+ * <p>The size of the guide table is {@code alpha * probabilities.length}.</p>
*
* @param rng Generator of uniformly distributed random numbers.
* @param probabilities The probabilities.
* @param alpha The alpha factor used to set the guide table size.
+ * @return the sampler
* @throws IllegalArgumentException if {@code probabilities} is null or empty, a
* probability is negative, infinite or {@code NaN}, the sum of all
* probabilities is not strictly positive, or {@code alpha} is not strictly positive.
*/
- public GuideTableDiscreteSampler(UniformRandomProvider rng,
- double[] probabilities,
- double alpha) {
+ public static SharedStateDiscreteSampler of(UniformRandomProvider rng,
+ double[] probabilities,
+ double alpha) {
validateParameters(probabilities, alpha);
final int size = probabilities.length;
- cumulativeProbabilities = new double[size];
+ final double[] cumulativeProbabilities = new double[size];
double sumProb = 0;
int count = 0;
@@ -110,12 +165,10 @@ public class GuideTableDiscreteSampler
throw new IllegalArgumentException("Invalid sum of probabilities: " + sumProb);
}
- this.rng = rng;
-
// Note: The guide table is at least length 1. Compute the size avoiding overflow
// in case (alpha * size) is too large.
final int guideTableSize = (int) Math.ceil(alpha * size);
- guideTable = new int[Math.max(guideTableSize, guideTableSize + 1)];
+ final int[] guideTable = new int[Math.max(guideTableSize, guideTableSize + 1)];
// Compute and store cumulative probability.
for (int x = 0; x < size; x++) {
@@ -123,7 +176,8 @@ public class GuideTableDiscreteSampler
cumulativeProbabilities[x] = (norm < 1) ? norm : 1.0;
// Set the guide table value as an exclusive upper bound (x + 1)
- guideTable[getGuideTableIndex(cumulativeProbabilities[x])] = x + 1;
+ final int index = getGuideTableIndex(cumulativeProbabilities[x], guideTable.length);
+ guideTable[index] = x + 1;
}
// Edge case for round-off
@@ -139,17 +193,8 @@ public class GuideTableDiscreteSampler
for (int i = 1; i < guideTable.length; i++) {
guideTable[i] = Math.max(guideTable[i - 1], guideTable[i]);
}
- }
- /**
- * @param rng Generator of uniformly distributed random numbers.
- * @param source Source to copy.
- */
- private GuideTableDiscreteSampler(UniformRandomProvider rng,
- GuideTableDiscreteSampler source) {
- this.rng = rng;
- cumulativeProbabilities = source.cumulativeProbabilities;
- guideTable = source.guideTable;
+ return new GuideTableDiscreteSampler(rng, cumulativeProbabilities, guideTable);
}
/**
@@ -171,48 +216,15 @@ public class GuideTableDiscreteSampler
/**
* Gets the guide table index for the probability. This is obtained using
- * {@code p * (guideTable.length - 1)} so is inside the length of the table.
+ * {@code p * (tableLength - 1)} so is inside the length of the table.
*
* @param p Cumulative probability.
+ * @param tableLength Table length.
* @return the guide table index.
*/
- private int getGuideTableIndex(double p) {
+ private static int getGuideTableIndex(double p, int tableLength) {
// Note: This is only ever called when p is in the range of the cumulative
// probability table. So assume 0 <= p <= 1.
- return (int) (p * (guideTable.length - 1));
- }
-
- /** {@inheritDoc} */
- @Override
- public int sample() {
- // Compute a probability
- final double u = rng.nextDouble();
-
- // Initialise the search using the guide table to find an initial guess.
- // The table provides an upper bound on the sample (x+1) for a known
- // cumulative probability (f(x)).
- int x = guideTable[getGuideTableIndex(u)];
- // Search down.
- // In the edge case where u is 1.0 then 'x' will be 1 outside the range of the
- // cumulative probability table and this will decrement to a valid range.
- // In the case where 'u' is mapped to the same guide table index as a lower
- // cumulative probability f(x) (due to rounding down) then this will not decrement
- // and return the exclusive upper bound (x+1).
- while (x != 0 && u <= cumulativeProbabilities[x - 1]) {
- x--;
- }
- return x;
- }
-
- /** {@inheritDoc} */
- @Override
- public String toString() {
- return "Guide table deviate [" + rng.toString() + "]";
- }
-
- /** {@inheritDoc} */
- @Override
- public SharedStateDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
- return new GuideTableDiscreteSampler(rng, this);
+ return (int) (p * (tableLength - 1));
}
}
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/KempSmallMeanPoissonSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/KempSmallMeanPoissonSampler.java
index 7c4a09c..353651d 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/KempSmallMeanPoissonSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/KempSmallMeanPoissonSampler.java
@@ -41,11 +41,11 @@ import org.apache.commons.rng.UniformRandomProvider;
* <p>Sampling uses 1 call to {@link UniformRandomProvider#nextDouble()}. This method provides
* an alternative to the {@link SmallMeanPoissonSampler} for slow generators of {@code double}.</p>
*
- * @since 1.3
* @see <a href="https://www.jstor.org/stable/2346348">Kemp, A.W. (1981) JRSS Vol. 30, pp.
* 249-253</a>
+ * @since 1.3
*/
-public class KempSmallMeanPoissonSampler
+public final class KempSmallMeanPoissonSampler
implements SharedStateDiscreteSampler {
/** Underlying source of randomness. */
private final UniformRandomProvider rng;
@@ -61,35 +61,15 @@ public class KempSmallMeanPoissonSampler
/**
* @param rng Generator of uniformly distributed random numbers.
+ * @param p0 Probability of the Poisson sample {@code p(x=0)}.
* @param mean Mean.
- * @throws IllegalArgumentException if {@code mean <= 0} or
- * {@code Math.exp(-mean) == 0}.
- */
- public KempSmallMeanPoissonSampler(UniformRandomProvider rng,
- double mean) {
- if (mean <= 0) {
- throw new IllegalArgumentException("Mean is not strictly positive: " + mean);
- }
-
- p0 = Math.exp(-mean);
- if (p0 > 0) {
- this.rng = rng;
- this.mean = mean;
- } else {
- // This catches the edge case of a NaN mean
- throw new IllegalArgumentException("No probability for mean " + mean);
- }
- }
-
- /**
- * @param rng Generator of uniformly distributed random numbers.
- * @param source Source to copy.
*/
private KempSmallMeanPoissonSampler(UniformRandomProvider rng,
- KempSmallMeanPoissonSampler source) {
+ double p0,
+ double mean) {
this.rng = rng;
- p0 = source.p0;
- mean = source.mean;
+ this.p0 = p0;
+ this.mean = mean;
}
/** {@inheritDoc} */
@@ -129,6 +109,32 @@ public class KempSmallMeanPoissonSampler
/** {@inheritDoc} */
@Override
public SharedStateDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
- return new KempSmallMeanPoissonSampler(rng, this);
+ return new KempSmallMeanPoissonSampler(rng, p0, mean);
+ }
+
+ /**
+ * Creates a new sampler for the Poisson distribution.
+ *
+ * @param rng Generator of uniformly distributed random numbers.
+ * @param mean Mean of the distribution.
+ * @return the sampler
+ * @throws IllegalArgumentException if {@code mean <= 0} or
+ * {@code Math.exp(-mean) == 0}.
+ */
+ public static SharedStateDiscreteSampler of(UniformRandomProvider rng,
+ double mean) {
+ if (mean <= 0) {
+ throw new IllegalArgumentException("Mean is not strictly positive: " + mean);
+ }
+
+ final double p0 = Math.exp(-mean);
+
+ // Probability must be positive. As mean increases then p(0) decreases.
+ if (p0 > 0) {
+ return new KempSmallMeanPoissonSampler(rng, p0, mean);
+ }
+
+ // This catches the edge case of a NaN mean
+ throw new IllegalArgumentException("No probability for mean: " + mean);
}
}
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/LargeMeanPoissonSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/LargeMeanPoissonSampler.java
index 2a68e32..bc71e5b 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/LargeMeanPoissonSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/LargeMeanPoissonSampler.java
@@ -155,7 +155,7 @@ public class LargeMeanPoissonSampler
final double lambdaFractional = mean - lambda;
smallMeanPoissonSampler = (lambdaFractional < Double.MIN_VALUE) ?
NO_SMALL_MEAN_POISSON_SAMPLER : // Not used.
- new KempSmallMeanPoissonSampler(rng, lambdaFractional);
+ KempSmallMeanPoissonSampler.of(rng, lambdaFractional);
}
/**
@@ -196,7 +196,7 @@ public class LargeMeanPoissonSampler
// The algorithm requires a Poisson sample from the remaining lambda fraction.
smallMeanPoissonSampler = (lambdaFractional < Double.MIN_VALUE) ?
NO_SMALL_MEAN_POISSON_SAMPLER : // Not used.
- new KempSmallMeanPoissonSampler(rng, lambdaFractional);
+ KempSmallMeanPoissonSampler.of(rng, lambdaFractional);
}
/**
diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/MarsagliaTsangWangDiscreteSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/MarsagliaTsangWangDiscreteSampler.java
index 32bc12e..97b9228 100644
--- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/MarsagliaTsangWangDiscreteSampler.java
+++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/distribution/MarsagliaTsangWangDiscreteSampler.java
@@ -40,17 +40,16 @@ import org.apache.commons.rng.UniformRandomProvider;
* <p>The sampler supports the following distributions:</p>
*
* <ul>
- * <li>Any discrete probability distribution (probabilities must be provided)
+ * <li>Enumerated distribution (probabilities must be provided for each sample)
* <li>Poisson distribution up to {@code mean = 1024}
* <li>Binomial distribution up to {@code trials = 65535}
* </ul>
*
- * @since 1.3
* @see <a href="http://dx.doi.org/10.18637/jss.v011.i03">Margsglia, et al (2004) JSS Vol.
* 11, Issue 3</a>
+ * @since 1.3
*/
-public abstract class MarsagliaTsangWangDiscreteSampler
- implements SharedStateDiscreteSampler {
+public final class MarsagliaTsangWangDiscreteSampler {
/** The value 2<sup>8</sup> as an {@code int}. */
private static final int INT_8 = 1 << 8;
/** The value 2<sup>16</sup> as an {@code int}. */
@@ -60,34 +59,6 @@ public abstract class MarsagliaTsangWangDiscreteSampler
/** The value 2<sup>31</sup> as a {@code double}. */
private static final double DOUBLE_31 = 1L << 31;
- /** The general name of any discrete probability distribution. */
- private static final String DISCRETE_NAME = "discrete";
- /** The name of the Poisson distribution. */
- private static final String POISSON_NAME = "Poisson";
- /** The name of the Binomial distribution. */
- private static final String BINOMIAL_NAME = "Binomial";
-
- /**
- * Upper bound on the mean for the Poisson distribution.
- *
- * <p>The original source code provided in Marsaglia, et al (2004) has no explicit
- * limit but the code fails at mean >= 1941 as the transform to compute p(x=mode)
- * produces infinity. Use a conservative limit of 1024.</p>
- */
- private static final double MAX_POISSON_MEAN = 1024;
- /**
- * The threshold for the mean of the Poisson distribution to switch the method used
- * to compute the probabilities. This is taken from the example software provided by
- * Marsaglia, et al (2004).
- */
- private static final double POISSON_MEAN_THRESHOLD = 21.4;
-
- /** Underlying source of randomness. */
- protected final UniformRandomProvider rng;
-
- /** The name of the distribution. */
- private final String distributionName;
-
// =========================================================================
// Implementation note:
//
@@ -109,15 +80,55 @@ public abstract class MarsagliaTsangWangDiscreteSampler
// when provided via an array of probabilities and the Poisson and Binomial
// distributions for a restricted set of parameters. The restrictions are
// imposed by the requirement to compute the entire probability distribution
- // from the controlling parameter(s) using a recursive method.
+ // from the controlling parameter(s) using a recursive method. Factory
+ // constructors return a SharedStateDiscreteSampler instance. Each distribution
+ // type is contained in an inner class.
// =========================================================================
/**
+ * The base class for Marsaglia-Tsang-Wang samplers.
+ */
+ private abstract static class AbstractMarsagliaTsangWangDiscreteSampler
+ implements SharedStateDiscreteSampler {
+ /** Underlying source of randomness. */
+ protected final UniformRandomProvider rng;
+
+ /** The name of the distribution. */
+ private final String distributionName;
+
+ /**
+ * @param rng Generator of uniformly distributed random numbers.
+ * @param distributionName Distribution name.
+ */
+ AbstractMarsagliaTsangWangDiscreteSampler(UniformRandomProvider rng,
+ String distributionName) {
+ this.rng = rng;
+ this.distributionName = distributionName;
+ }
+
+ /**
+ * @param rng Generator of uniformly distributed random numbers.
+ * @param source Source to copy.
+ */
+ AbstractMarsagliaTsangWangDiscreteSampler(UniformRandomProvider rng,
+ AbstractMarsagliaTsangWangDiscreteSampler source) {
+ this.rng = rng;
+ this.distributionName = source.distributionName;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ return "Marsaglia Tsang Wang " + distributionName + " deviate [" + rng.toString() + "]";
+ }
+ }
+
+ /**
* An implementation for the sample algorithm based on the decomposition of the
* index in the range {@code [0,2^30)} into 5 base-64 digits with 8-bit backing storage.
*/
private static class MarsagliaTsangWangBase64Int8DiscreteSampler
- extends MarsagliaTsangWangDiscreteSampler {
+ extends AbstractMarsagliaTsangWangDiscreteSampler {
/** The mask to convert a {@code byte} to an unsigned 8-bit integer. */
private static final int MASK = 0xff;
@@ -257,7 +268,7 @@ public abstract class MarsagliaTsangWangDiscreteSampler
* index in the range {@code [0,2^30)} into 5 base-64 digits with 16-bit backing storage.
*/
private static class MarsagliaTsangWangBase64Int16DiscreteSampler
- extends MarsagliaTsangWangDiscreteSampler {
+ extends AbstractMarsagliaTsangWangDiscreteSampler {
/** The mask to convert a {@code byte} to an unsigned 16-bit integer. */
private static final int MASK = 0xffff;
@@ -397,7 +408,7 @@ public abstract class MarsagliaTsangWangDiscreteSampler
* index in the range {@code [0,2^30)} into 5 base-64 digits with 32-bit backing storage.
*/
private static class MarsagliaTsangWangBase64Int32DiscreteSampler
- extends MarsagliaTsangWangDiscreteSampler {
+ extends AbstractMarsagliaTsangWangDiscreteSampler {
/** Limit for look-up table 1. */
private final int t1;
/** Limit for look-up table 2. */
@@ -528,108 +539,10 @@ public abstract class MarsagliaTsangWangDiscreteSampler
}
}
- /**
- * Return a fixed result for the Binomial distribution. This is a special class to handle
- * an edge case of probability of success equal to 0 or 1.
- */
- private static class MarsagliaTsangWangFixedResultBinomialSampler
- extends MarsagliaTsangWangDiscreteSampler {
- /** The result. */
- private final int result;
-
- /**
- * @param result Result.
- */
- MarsagliaTsangWangFixedResultBinomialSampler(int result) {
- super(null, BINOMIAL_NAME);
- this.result = result;
- }
-
- @Override
- public int sample() {
- return result;
- }
-
- @Override
- public String toString() {
- return BINOMIAL_NAME + " deviate";
- }
-
- @Override
- public SharedStateDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
- // No shared state
- return this;
- }
- }
-
- /**
- * Return an inversion result for the Binomial distribution. This assumes the
- * following:
- *
- * <pre>
- * Binomial(n, p) = 1 - Binomial(n, 1 - p)
- * </pre>
- */
- private static class MarsagliaTsangWangInversionBinomialSampler
- extends MarsagliaTsangWangDiscreteSampler {
- /** The number of trials. */
- private final int trials;
- /** The Binomial distribution sampler. */
- private final SharedStateDiscreteSampler sampler;
-
- /**
- * @param trials Number of trials.
- * @param sampler Binomial distribution sampler.
- */
- MarsagliaTsangWangInversionBinomialSampler(int trials,
- SharedStateDiscreteSampler sampler) {
- super(null, BINOMIAL_NAME);
- this.trials = trials;
- this.sampler = sampler;
- }
-
- @Override
- public int sample() {
- return trials - sampler.sample();
- }
-
- @Override
- public String toString() {
- return sampler.toString();
- }
-
- @Override
- public SharedStateDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
- return new MarsagliaTsangWangInversionBinomialSampler(this.trials,
- this.sampler.withUniformRandomProvider(rng));
- }
- }
-
- /**
- * @param rng Generator of uniformly distributed random numbers.
- * @param distributionName Distribution name.
- */
- MarsagliaTsangWangDiscreteSampler(UniformRandomProvider rng,
- String distributionName) {
- this.rng = rng;
- this.distributionName = distributionName;
- }
- /**
- * @param rng Generator of uniformly distributed random numbers.
- * @param source Source to copy.
- */
- MarsagliaTsangWangDiscreteSampler(UniformRandomProvider rng,
- MarsagliaTsangWangDiscreteSampler source) {
- this.rng = rng;
- this.distributionName = source.distributionName;
- }
- /** {@inheritDoc} */
- @Override
- public String toString() {
- return "Marsaglia Tsang Wang " + distributionName + " deviate [" + rng.toString() + "]";
- }
+ /** Class contains only static methods. */
+ private MarsagliaTsangWangDiscreteSampler() {}
/**
* Gets the k<sup>th</sup> base 64 digit of {@code m}.
@@ -643,6 +556,17 @@ public abstract class MarsagliaTsangWangDiscreteSampler
}
/**
+ * Convert the probability to an integer in the range [0,2^30]. This is the numerator of
+ * a fraction with assumed denominator 2<sup>30</sup>.
+ *
+ * @param p Probability.
+ * @return the fraction numerator
+ */
+ private static int toUnsignedInt30(double p) {
+ return (int) (p * INT_30 + 0.5);
+ }
+
+ /**
* Create a new instance for probabilities {@code p(i)} where the sample value {@code x} is
* {@code i + offset}.
*
@@ -655,10 +579,10 @@ public abstract class MarsagliaTsangWangDiscreteSampler
* @param offset The offset (must be positive).
* @return Sampler.
*/
- private static MarsagliaTsangWangDiscreteSampler createSampler(UniformRandomProvider rng,
- String distributionName,
- int[] prob,
- int offset) {
+ private static SharedStateDiscreteSampler createSampler(UniformRandomProvider rng,
+ String distributionName,
+ int[] prob,
+ int offset) {
// Note: No argument checks for private method.
// Choose implementation based on the maximum index
@@ -673,426 +597,545 @@ public abstract class MarsagliaTsangWangDiscreteSampler
}
// =========================================================================
- // The following factory methods are the public API to construct a sampler for:
- // - Any discrete probability distribution (from provided double[] probabilities)
+ // The following public classes provide factory methods to construct a sampler for:
+ // - Enumerated probability distribution (from provided double[] probabilities)
// - Poisson distribution for mean <= 1024
// - Binomial distribution for trials <= 65535
// =========================================================================
/**
- * Creates a sampler for a given probability distribution.
- *
- * <p>The probabilities will be normalised using their sum. The only requirement
- * is the sum is positive.</p>
- *
- * <p>The sum of the probabilities is normalised to 2<sup>30</sup>. Note that
- * probabilities are adjusted to the nearest 1<sup>-30</sup> due to round-off during
- * the normalisation conversion. Consequently any probability less than 2<sup>-31</sup>
- * will not be observed in samples.</p>
- *
- * @param rng Generator of uniformly distributed random numbers.
- * @param probabilities The list of probabilities.
- * @return Sampler.
- * @throws IllegalArgumentException if {@code probabilities} is null or empty, a
- * probability is negative, infinite or {@code NaN}, or the sum of all
- * probabilities is not strictly positive.
+ * Create a sampler for an enumerated distribution of {@code n} values each with an
+ * associated probability.
+ * The samples corresponding to each probability are assumed to be a natural sequence
+ * starting at zero.
*/
- public static MarsagliaTsangWangDiscreteSampler createDiscreteDistribution(UniformRandomProvider rng,
- double[] probabilities) {
- return createSampler(rng, DISCRETE_NAME, normaliseProbabilities(probabilities), 0);
- }
+ public static final class Enumerated {
+ /** The name of the enumerated probability distribution. */
+ private static final String ENUMERATED_NAME = "Enumerated";
- /**
- * Normalise the probabilities to integers that sum to 2<sup>30</sup>.
- *
- * @param probabilities The list of probabilities.
- * @return the normalised probabilities.
- * @throws IllegalArgumentException if {@code probabilities} is null or empty, a
- * probability is negative, infinite or {@code NaN}, or the sum of all
- * probabilities is not strictly positive.
- */
- private static int[] normaliseProbabilities(double[] probabilities) {
- final double sumProb = InternalUtils.validateProbabilities(probabilities);
-
- // Compute the normalisation: 2^30 / sum
- final double normalisation = INT_30 / sumProb;
- final int[] prob = new int[probabilities.length];
- int sum = 0;
- int max = 0;
- int mode = 0;
- for (int i = 0; i < prob.length; i++) {
- // Add 0.5 for rounding
- final int p = (int) (probabilities[i] * normalisation + 0.5);
- sum += p;
- // Find the mode (maximum probability)
- if (max < p) {
- max = p;
- mode = i;
- }
- prob[i] = p;
- }
+ /** Class contains only static methods. */
+ private Enumerated() {}
- // The sum must be >= 2^30.
- // Here just compensate the difference onto the highest probability.
- prob[mode] += INT_30 - sum;
+ /**
+ * Creates a sampler for an enumerated distribution of {@code n} values each with an
+ * associated probability.
+ *
+ * <p>The probabilities will be normalised using their sum. The only requirement
+ * is the sum is positive.</p>
+ *
+ * <p>The sum of the probabilities is normalised to 2<sup>30</sup>. Note that
+ * probabilities are adjusted to the nearest 1<sup>-30</sup> due to round-off during
+ * the normalisation conversion. Consequently any probability less than 2<sup>-31</sup>
+ * will not be observed in samples.</p>
+ *
+ * @param rng Generator of uniformly distributed random numbers.
+ * @param probabilities The list of probabilities.
+ * @return Sampler.
+ * @throws IllegalArgumentException if {@code probabilities} is null or empty, a
+ * probability is negative, infinite or {@code NaN}, or the sum of all
+ * probabilities is not strictly positive.
+ */
+ public static SharedStateDiscreteSampler of(UniformRandomProvider rng,
+ double[] probabilities) {
+ return createSampler(rng, ENUMERATED_NAME, normaliseProbabilities(probabilities), 0);
+ }
- return prob;
- }
+ /**
+ * Normalise the probabilities to integers that sum to 2<sup>30</sup>.
+ *
+ * @param probabilities The list of probabilities.
+ * @return the normalised probabilities.
+ * @throws IllegalArgumentException if {@code probabilities} is null or empty, a
+ * probability is negative, infinite or {@code NaN}, or the sum of all
+ * probabilities is not strictly positive.
+ */
+ private static int[] normaliseProbabilities(double[] probabilities) {
+ final double sumProb = InternalUtils.validateProbabilities(probabilities);
+
+ // Compute the normalisation: 2^30 / sum
+ final double normalisation = INT_30 / sumProb;
+ final int[] prob = new int[probabilities.length];
+ int sum = 0;
+ int max = 0;
+ int mode = 0;
+ for (int i = 0; i < prob.length; i++) {
+ // Add 0.5 for rounding
+ final int p = (int) (probabilities[i] * normalisation + 0.5);
+ sum += p;
+ // Find the mode (maximum probability)
+ if (max < p) {
+ max = p;
+ mode = i;
+ }
+ prob[i] = p;
+ }
- /**
- * Creates a sampler for the Poisson distribution.
- *
- * <p>Any probability less than 2<sup>-31</sup> will not be observed in samples.</p>
- *
- * <p>Storage requirements depend on the tabulated probability values. Example storage
- * requirements are listed below.</p>
- *
- * <pre>
- * mean table size kB
- * 0.25 882 0.88
- * 0.5 1135 1.14
- * 1 1200 1.20
- * 2 1451 1.45
- * 4 1955 1.96
- * 8 2961 2.96
- * 16 4410 4.41
- * 32 6115 6.11
- * 64 8499 8.50
- * 128 11528 11.53
- * 256 15935 31.87
- * 512 20912 41.82
- * 1024 30614 61.23
- * </pre>
- *
- * <p>Note: Storage changes to 2 bytes per index between {@code mean=128} and {@code mean=256}.</p>
- *
- * @param rng Generator of uniformly distributed random numbers.
- * @param mean Mean.
- * @return Sampler.
- * @throws IllegalArgumentException if {@code mean <= 0} or {@code mean > 1024}.
- */
- public static MarsagliaTsangWangDiscreteSampler createPoissonDistribution(UniformRandomProvider rng,
- double mean) {
- validatePoissonDistributionParameters(mean);
-
- // Create the distribution either from X=0 or from X=mode when the mean is high.
- return mean < POISSON_MEAN_THRESHOLD ?
- createPoissonDistributionFromX0(rng, mean) :
- createPoissonDistributionFromXMode(rng, mean);
- }
+ // The sum must be >= 2^30.
+ // Here just compensate the difference onto the highest probability.
+ prob[mode] += INT_30 - sum;
- /**
- * Validate the Poisson distribution parameters.
- *
- * @param mean Mean.
- * @throws IllegalArgumentException if {@code mean <= 0} or {@code mean > 1024}.
- */
- private static void validatePoissonDistributionParameters(double mean) {
- if (mean <= 0) {
- throw new IllegalArgumentException("mean is not strictly positive: " + mean);
- }
- if (mean > MAX_POISSON_MEAN) {
- throw new IllegalArgumentException("mean " + mean + " > " + MAX_POISSON_MEAN);
+ return prob;
}
}
/**
- * Creates the Poisson distribution by computing probabilities recursively from {@code X=0}.
- *
- * @param rng Generator of uniformly distributed random numbers.
- * @param mean Mean.
- * @return Sampler.
+ * Create a sampler for the Poisson distribution.
*/
- private static MarsagliaTsangWangDiscreteSampler createPoissonDistributionFromX0(
- UniformRandomProvider rng, double mean) {
- final double p0 = Math.exp(-mean);
-
- // Recursive update of Poisson probability until the value is too small
- // p(x + 1) = p(x) * mean / (x + 1)
- double p = p0;
- int i;
- for (i = 1; p * DOUBLE_31 >= 1; i++) {
- p *= mean / i;
- }
+ public static final class Poisson {
+ /** The name of the Poisson distribution. */
+ private static final String POISSON_NAME = "Poisson";
- // Probabilities are 30-bit integers, assumed denominator 2^30
- final int size = i - 1;
- final int[] prob = new int[size];
-
- p = p0;
- prob[0] = toUnsignedInt30(p);
- // The sum must exceed 2^30. In edges cases this is false due to round-off.
- int sum = prob[0];
- for (i = 1; i < prob.length; i++) {
- p *= mean / i;
- prob[i] = toUnsignedInt30(p);
- sum += prob[i];
- }
+ /**
+ * Upper bound on the mean for the Poisson distribution.
+ *
+ * <p>The original source code provided in Marsaglia, et al (2004) has no explicit
+ * limit but the code fails at mean >= 1941 as the transform to compute p(x=mode)
+ * produces infinity. Use a conservative limit of 1024.</p>
+ */
- // If the sum is < 2^30 add the remaining sum to the mode (floor(mean)).
- prob[(int) mean] += Math.max(0, INT_30 - sum);
+ private static final double MAX_MEAN = 1024;
+ /**
+ * The threshold for the mean of the Poisson distribution to switch the method used
+ * to compute the probabilities. This is taken from the example software provided by
+ * Marsaglia, et al (2004).
+ */
+ private static final double MEAN_THRESHOLD = 21.4;
- // Note: offset = 0
- return createSampler(rng, POISSON_NAME, prob, 0);
- }
+ /** Class contains only static methods. */
+ private Poisson() {}
- /**
- * Creates the Poisson distribution by computing probabilities recursively upward and downward
- * from {@code X=mode}, the location of the largest p-value.
- *
- * @param rng Generator of uniformly distributed random numbers.
- * @param mean Mean.
- * @return Sampler.
- */
- private static MarsagliaTsangWangDiscreteSampler createPoissonDistributionFromXMode(
- UniformRandomProvider rng, double mean) {
- // If mean >= 21.4, generate from largest p-value up, then largest down.
- // The largest p-value will be at the mode (floor(mean)).
-
- // Find p(x=mode)
- final int mode = (int) mean;
- // This transform is stable until mean >= 1941 where p will result in Infinity
- // before the divisor i is large enough to start reducing the product (i.e. i > c).
- final double c = mean * Math.exp(-mean / mode);
- double p = 1.0;
- int i;
- for (i = 1; i <= mode; i++) {
- p *= c / i;
+ /**
+ * Creates a sampler for the Poisson distribution.
+ *
+ * <p>Any probability less than 2<sup>-31</sup> will not be observed in samples.</p>
+ *
+ * <p>Storage requirements depend on the tabulated probability values. Example storage
+ * requirements are listed below.</p>
+ *
+ * <pre>
+ * mean table size kB
+ * 0.25 882 0.88
+ * 0.5 1135 1.14
+ * 1 1200 1.20
+ * 2 1451 1.45
+ * 4 1955 1.96
+ * 8 2961 2.96
+ * 16 4410 4.41
+ * 32 6115 6.11
+ * 64 8499 8.50
+ * 128 11528 11.53
+ * 256 15935 31.87
+ * 512 20912 41.82
+ * 1024 30614 61.23
+ * </pre>
+ *
+ * <p>Note: Storage changes to 2 bytes per index between {@code mean=128} and {@code mean=256}.</p>
+ *
+ * @param rng Generator of uniformly distributed random numbers.
+ * @param mean Mean.
+ * @return Sampler.
+ * @throws IllegalArgumentException if {@code mean <= 0} or {@code mean > 1024}.
+ */
+ public static SharedStateDiscreteSampler of(UniformRandomProvider rng,
+ double mean) {
+ validatePoissonDistributionParameters(mean);
+
+ // Create the distribution either from X=0 or from X=mode when the mean is high.
+ return mean < MEAN_THRESHOLD ?
+ createPoissonDistributionFromX0(rng, mean) :
+ createPoissonDistributionFromXMode(rng, mean);
}
- final double pMode = p;
- // Find the upper limit using recursive computation of the p-value.
- // Note this will exit when i overflows to negative so no check on the range
- for (i = mode + 1; p * DOUBLE_31 >= 1; i++) {
- p *= mean / i;
- }
- final int last = i - 2;
-
- // Find the lower limit using recursive computation of the p-value.
- p = pMode;
- int j = -1;
- for (i = mode - 1; i >= 0; i--) {
- p *= (i + 1) / mean;
- if (p * DOUBLE_31 < 1) {
- j = i;
- break;
+ /**
+ * Validate the Poisson distribution parameters.
+ *
+ * @param mean Mean.
+ * @throws IllegalArgumentException if {@code mean <= 0} or {@code mean > 1024}.
+ */
+ private static void validatePoissonDistributionParameters(double mean) {
+ if (mean <= 0) {
+ throw new IllegalArgumentException("mean is not strictly positive: " + mean);
+ }
+ if (mean > MAX_MEAN) {
+ throw new IllegalArgumentException("mean " + mean + " > " + MAX_MEAN);
}
}
- // Probabilities are 30-bit integers, assumed denominator 2^30.
- // This is the minimum sample value: prob[x - offset] = p(x)
- final int offset = j + 1;
- final int size = last - offset + 1;
- final int[] prob = new int[size];
-
- p = pMode;
- prob[mode - offset] = toUnsignedInt30(p);
- // The sum must exceed 2^30. In edges cases this is false due to round-off.
- int sum = prob[mode - offset];
- // From mode to upper limit
- for (i = mode + 1; i <= last; i++) {
- p *= mean / i;
- prob[i - offset] = toUnsignedInt30(p);
- sum += prob[i - offset];
- }
- // From mode to lower limit
- p = pMode;
- for (i = mode - 1; i >= offset; i--) {
- p *= (i + 1) / mean;
- prob[i - offset] = toUnsignedInt30(p);
- sum += prob[i - offset];
+ /**
+ * Creates the Poisson distribution by computing probabilities recursively from {@code X=0}.
+ *
+ * @param rng Generator of uniformly distributed random numbers.
+ * @param mean Mean.
+ * @return Sampler.
+ */
+ private static SharedStateDiscreteSampler createPoissonDistributionFromX0(
+ UniformRandomProvider rng, double mean) {
+ final double p0 = Math.exp(-mean);
+
+ // Recursive update of Poisson probability until the value is too small
+ // p(x + 1) = p(x) * mean / (x + 1)
+ double p = p0;
+ int i;
+ for (i = 1; p * DOUBLE_31 >= 1; i++) {
+ p *= mean / i;
+ }
+
+ // Probabilities are 30-bit integers, assumed denominator 2^30
+ final int size = i - 1;
+ final int[] prob = new int[size];
+
+ p = p0;
+ prob[0] = toUnsignedInt30(p);
+ // The sum must exceed 2^30. In edges cases this is false due to round-off.
+ int sum = prob[0];
+ for (i = 1; i < prob.length; i++) {
+ p *= mean / i;
+ prob[i] = toUnsignedInt30(p);
+ sum += prob[i];
+ }
+
+ // If the sum is < 2^30 add the remaining sum to the mode (floor(mean)).
+ prob[(int) mean] += Math.max(0, INT_30 - sum);
+
+ // Note: offset = 0
+ return createSampler(rng, POISSON_NAME, prob, 0);
}
- // If the sum is < 2^30 add the remaining sum to the mode.
- // If above 2^30 then the effect is truncation of the long tail of the distribution.
- prob[mode - offset] += Math.max(0, INT_30 - sum);
+ /**
+ * Creates the Poisson distribution by computing probabilities recursively upward and downward
+ * from {@code X=mode}, the location of the largest p-value.
+ *
+ * @param rng Generator of uniformly distributed random numbers.
+ * @param mean Mean.
+ * @return Sampler.
+ */
+ private static SharedStateDiscreteSampler createPoissonDistributionFromXMode(
+ UniformRandomProvider rng, double mean) {
+ // If mean >= 21.4, generate from largest p-value up, then largest down.
+ // The largest p-value will be at the mode (floor(mean)).
+
+ // Find p(x=mode)
+ final int mode = (int) mean;
+ // This transform is stable until mean >= 1941 where p will result in Infinity
+ // before the divisor i is large enough to start reducing the product (i.e. i > c).
+ final double c = mean * Math.exp(-mean / mode);
+ double p = 1.0;
+ int i;
+ for (i = 1; i <= mode; i++) {
+ p *= c / i;
+ }
+ final double pMode = p;
+
+ // Find the upper limit using recursive computation of the p-value.
+ // Note this will exit when i overflows to negative so no check on the range
+ for (i = mode + 1; p * DOUBLE_31 >= 1; i++) {
+ p *= mean / i;
+ }
+ final int last = i - 2;
+
+ // Find the lower limit using recursive computation of the p-value.
+ p = pMode;
+ int j = -1;
+ for (i = mode - 1; i >= 0; i--) {
+ p *= (i + 1) / mean;
+ if (p * DOUBLE_31 < 1) {
+ j = i;
+ break;
+ }
+ }
- return createSampler(rng, POISSON_NAME, prob, offset);
+ // Probabilities are 30-bit integers, assumed denominator 2^30.
+ // This is the minimum sample value: prob[x - offset] = p(x)
+ final int offset = j + 1;
+ final int size = last - offset + 1;
+ final int[] prob = new int[size];
+
+ p = pMode;
+ prob[mode - offset] = toUnsignedInt30(p);
+ // The sum must exceed 2^30. In edges cases this is false due to round-off.
+ int sum = prob[mode - offset];
+ // From mode to upper limit
+ for (i = mode + 1; i <= last; i++) {
+ p *= mean / i;
+ prob[i - offset] = toUnsignedInt30(p);
+ sum += prob[i - offset];
+ }
+ // From mode to lower limit
+ p = pMode;
+ for (i = mode - 1; i >= offset; i--) {
+ p *= (i + 1) / mean;
+ prob[i - offset] = toUnsignedInt30(p);
+ sum += prob[i - offset];
+ }
+
+ // If the sum is < 2^30 add the remaining sum to the mode.
+ // If above 2^30 then the effect is truncation of the long tail of the distribution.
+ prob[mode - offset] += Math.max(0, INT_30 - sum);
+
+ return createSampler(rng, POISSON_NAME, prob, offset);
+ }
}
/**
- * Creates a sampler for the Binomial distribution.
- *
- * <p>Any probability less than 2<sup>-31</sup> will not be observed in samples.</p>
- *
- * <p>Storage requirements depend on the tabulated probability values. Example storage
- * requirements are listed below (in kB).</p>
- *
- * <pre>
- * p
- * trials 0.5 0.1 0.01 0.001
- * 4 0.06 0.63 0.44 0.44
- * 16 0.69 1.14 0.76 0.44
- * 64 4.73 2.40 1.14 0.51
- * 256 8.63 5.17 1.89 0.82
- * 1024 31.12 9.45 3.34 0.89
- * </pre>
- *
- * <p>The method requires that the Binomial distribution probability at {@code x=0} can be computed.
- * This will fail when {@code (1 - p)^trials == 0} which requires {@code trials} to be large
- * and/or {@code p} to be small. In this case an exception is raised.</p>
- *
- * @param rng Generator of uniformly distributed random numbers.
- * @param trials Number of trials.
- * @param probabilityOfSuccess Probability of success (p).
- * @return Sampler.
- * @throws IllegalArgumentException if {@code trials < 0} or {@code trials >= 2^16},
- * {@code p} is not in the range {@code [0-1]}, or the probability distribution cannot
- * be computed.
+ * Create a sampler for the Binomial distribution.
*/
- public static MarsagliaTsangWangDiscreteSampler createBinomialDistribution(UniformRandomProvider rng,
- int trials,
- double probabilityOfSuccess) {
- validateBinomialDistributionParameters(trials, probabilityOfSuccess);
-
- // Handle edge cases
- if (probabilityOfSuccess == 0) {
- return new MarsagliaTsangWangFixedResultBinomialSampler(0);
- }
- if (probabilityOfSuccess == 1) {
- return new MarsagliaTsangWangFixedResultBinomialSampler(trials);
- }
+ public static final class Binomial {
+ /** The name of the Binomial distribution. */
+ private static final String BINOMIAL_NAME = "Binomial";
+
+ /**
+ * Return a fixed result for the Binomial distribution. This is a special class to handle
+ * an edge case of probability of success equal to 0 or 1.
+ */
+ private static class MarsagliaTsangWangFixedResultBinomialSampler
+ extends AbstractMarsagliaTsangWangDiscreteSampler {
+ /** The result. */
+ private final int result;
+
+ /**
+ * @param result Result.
+ */
+ MarsagliaTsangWangFixedResultBinomialSampler(int result) {
+ super(null, BINOMIAL_NAME);
+ this.result = result;
+ }
- // Check the supported size.
- if (trials >= INT_16) {
- throw new IllegalArgumentException("Unsupported number of trials: " + trials);
+ @Override
+ public int sample() {
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return BINOMIAL_NAME + " deviate";
+ }
+
+ @Override
+ public SharedStateDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
+ // No shared state
+ return this;
+ }
}
- return createBinomialDistributionSampler(rng, trials, probabilityOfSuccess);
- }
+ /**
+ * Return an inversion result for the Binomial distribution. This assumes the
+ * following:
+ *
+ * <pre>
+ * Binomial(n, p) = 1 - Binomial(n, 1 - p)
+ * </pre>
+ */
+ private static class MarsagliaTsangWangInversionBinomialSampler
+ extends AbstractMarsagliaTsangWangDiscreteSampler {
+ /** The number of trials. */
+ private final int trials;
+ /** The Binomial distribution sampler. */
+ private final SharedStateDiscreteSampler sampler;
+
+ /**
+ * @param trials Number of trials.
+ * @param sampler Binomial distribution sampler.
+ */
+ MarsagliaTsangWangInversionBinomialSampler(int trials,
+ SharedStateDiscreteSampler sampler) {
+ super(null, BINOMIAL_NAME);
+ this.trials = trials;
+ this.sampler = sampler;
+ }
- /**
- * Validate the Binomial distribution parameters.
- *
- * @param trials Number of trials.
- * @param probabilityOfSuccess Probability of success (p).
- * @throws IllegalArgumentException if {@code trials < 0} or
- * {@code p} is not in the range {@code [0-1]}
- */
- private static void validateBinomialDistributionParameters(int trials, double probabilityOfSuccess) {
- if (trials < 0) {
- throw new IllegalArgumentException("Trials is not positive: " + trials);
+ @Override
+ public int sample() {
+ return trials - sampler.sample();
+ }
+
+ @Override
+ public String toString() {
+ return sampler.toString();
+ }
+
+ @Override
+ public SharedStateDiscreteSampler withUniformRandomProvider(UniformRandomProvider rng) {
+ return new MarsagliaTsangWangInversionBinomialSampler(this.trials,
+ this.sampler.withUniformRandomProvider(rng));
+ }
}
- if (probabilityOfSuccess < 0 || probabilityOfSuccess > 1) {
- throw new IllegalArgumentException("Probability is not in range [0,1]: " + probabilityOfSuccess);
+
+ /** Class contains only static methods. */
+ private Binomial() {}
+
+ /**
+ * Creates a sampler for the Binomial distribution.
+ *
+ * <p>Any probability less than 2<sup>-31</sup> will not be observed in samples.</p>
+ *
+ * <p>Storage requirements depend on the tabulated probability values. Example storage
+ * requirements are listed below (in kB).</p>
+ *
+ * <pre>
+ * p
+ * trials 0.5 0.1 0.01 0.001
+ * 4 0.06 0.63 0.44 0.44
+ * 16 0.69 1.14 0.76 0.44
+ * 64 4.73 2.40 1.14 0.51
+ * 256 8.63 5.17 1.89 0.82
+ * 1024 31.12 9.45 3.34 0.89
+ * </pre>
+ *
+ * <p>The method requires that the Binomial distribution probability at {@code x=0} can be computed.
+ * This will fail when {@code (1 - p)^trials == 0} which requires {@code trials} to be large
+ * and/or {@code p} to be small. In this case an exception is raised.</p>
+ *
+ * @param rng Generator of uniformly distributed random numbers.
+ * @param trials Number of trials.
+ * @param probabilityOfSuccess Probability of success (p).
+ * @return Sampler.
+ * @throws IllegalArgumentException if {@code trials < 0} or {@code trials >= 2^16},
+ * {@code p} is not in the range {@code [0-1]}, or the probability distribution cannot
+ * be computed.
+ */
+ public static SharedStateDiscreteSampler of(UniformRandomProvider rng,
+ int trials,
+ double probabilityOfSuccess) {
+ validateBinomialDistributionParameters(trials, probabilityOfSuccess);
+
+ // Handle edge cases
+ if (probabilityOfSuccess == 0) {
+ return new MarsagliaTsangWangFixedResultBinomialSampler(0);
+ }
+ if (probabilityOfSuccess == 1) {
+ return new MarsagliaTsangWangFixedResultBinomialSampler(trials);
+ }
+
+ // Check the supported size.
+ if (trials >= INT_16) {
+ throw new IllegalArgumentException("Unsupported number of trials: " + trials);
+ }
+
+ return createBinomialDistributionSampler(rng, trials, probabilityOfSuccess);
}
- }
- /**
- * Creates the Binomial distribution sampler.
- *
- * <p>This assumes the parameters for the distribution are valid. The method
- * will only fail if the initial probability for {@code X=0} is zero.</p>
- *
- * @param rng Generator of uniformly distributed random numbers.
- * @param trials Number of trials.
- * @param probabilityOfSuccess Probability of success (p).
- * @return Sampler.
- * @throws IllegalArgumentException if the probability distribution cannot be
- * computed.
- */
- private static MarsagliaTsangWangDiscreteSampler createBinomialDistributionSampler(
- UniformRandomProvider rng, int trials, double probabilityOfSuccess) {
-
- // The maximum supported value for Math.exp is approximately -744.
- // This occurs when trials is large and p is close to 1.
- // Handle this by using an inversion: generate j=Binomial(n,1-p), return n-j
- final boolean useInversion = probabilityOfSuccess > 0.5;
- final double p = useInversion ? 1 - probabilityOfSuccess : probabilityOfSuccess;
-
- // Check if the distribution can be computed
- final double p0 = Math.exp(trials * Math.log(1 - p));
- if (p0 < Double.MIN_VALUE) {
- throw new IllegalArgumentException("Unable to compute distribution");
+ /**
+ * Validate the Binomial distribution parameters.
+ *
+ * @param trials Number of trials.
+ * @param probabilityOfSuccess Probability of success (p).
+ * @throws IllegalArgumentException if {@code trials < 0} or
+ * {@code p} is not in the range {@code [0-1]}
+ */
+ private static void validateBinomialDistributionParameters(int trials, double probabilityOfSuccess) {
+ if (trials < 0) {
+ throw new IllegalArgumentException("Trials is not positive: " + trials);
+ }
+ if (probabilityOfSuccess < 0 || probabilityOfSuccess > 1) {
+ throw new IllegalArgumentException("Probability is not in range [0,1]: " + probabilityOfSuccess);
+ }
}
- // First find size of probability array
- double t = p0;
- final double h = p / (1 - p);
- // Find first probability above the threshold of 2^-31
- int begin = 0;
- if (t * DOUBLE_31 < 1) {
- // Somewhere after p(0)
- // Note:
- // If this loop is entered p(0) is < 2^-31.
- // This has been tested at the extreme for p(0)=Double.MIN_VALUE and either
- // p=0.5 or trials=2^16-1 and does not fail to find the beginning.
- for (int i = 1; i <= trials; i++) {
+ /**
+ * Creates the Binomial distribution sampler.
+ *
+ * <p>This assumes the parameters for the distribution are valid. The method
+ * will only fail if the initial probability for {@code X=0} is zero.</p>
+ *
+ * @param rng Generator of uniformly distributed random numbers.
+ * @param trials Number of trials.
+ * @param probabilityOfSuccess Probability of success (p).
+ * @return Sampler.
+ * @throws IllegalArgumentException if the probability distribution cannot be
+ * computed.
+ */
+ private static SharedStateDiscreteSampler createBinomialDistributionSampler(
+ UniformRandomProvider rng, int trials, double probabilityOfSuccess) {
+
+ // The maximum supported value for Math.exp is approximately -744.
+ // This occurs when trials is large and p is close to 1.
+ // Handle this by using an inversion: generate j=Binomial(n,1-p), return n-j
+ final boolean useInversion = probabilityOfSuccess > 0.5;
+ final double p = useInversion ? 1 - probabilityOfSuccess : probabilityOfSuccess;
+
+ // Check if the distribution can be computed
+ final double p0 = Math.exp(trials * Math.log(1 - p));
+ if (p0 < Double.MIN_VALUE) {
+ throw new IllegalArgumentException("Unable to compute distribution");
+ }
+
+ // First find size of probability array
+ double t = p0;
+ final double h = p / (1 - p);
+ // Find first probability above the threshold of 2^-31
+ int begin = 0;
+ if (t * DOUBLE_31 < 1) {
+ // Somewhere after p(0)
+ // Note:
+ // If this loop is entered p(0) is < 2^-31.
+ // This has been tested at the extreme for p(0)=Double.MIN_VALUE and either
+ // p=0.5 or trials=2^16-1 and does not fail to find the beginning.
+ for (int i = 1; i <= trials; i++) {
+ t *= (trials + 1 - i) * h / i;
+ if (t * DOUBLE_31 >= 1) {
+ begin = i;
+ break;
+ }
+ }
+ }
+ // Find last probability
+ int end = trials;
+ for (int i = begin + 1; i <= trials; i++) {
t *= (trials + 1 - i) * h / i;
- if (t * DOUBLE_31 >= 1) {
- begin = i;
+ if (t * DOUBLE_31 < 1) {
+ end = i - 1;
break;
}
}
- }
- // Find last probability
- int end = trials;
- for (int i = begin + 1; i <= trials; i++) {
- t *= (trials + 1 - i) * h / i;
- if (t * DOUBLE_31 < 1) {
- end = i - 1;
- break;
- }
- }
-
- return createBinomialDistributionSamplerFromRange(rng, trials, p, useInversion,
- p0, begin, end);
- }
- /**
- * Creates the Binomial distribution sampler using only the probability values for {@code X}
- * between the begin and the end (inclusive).
- *
- * @param rng Generator of uniformly distributed random numbers.
- * @param trials Number of trials.
- * @param p Probability of success (p).
- * @param useInversion Set to {@code true} if the probability was inverted.
- * @param p0 Probability at {@code X=0}
- * @param begin Begin value {@code X} for the distribution.
- * @param end End value {@code X} for the distribution.
- * @return Sampler.
- */
- private static MarsagliaTsangWangDiscreteSampler createBinomialDistributionSamplerFromRange(
- UniformRandomProvider rng, int trials, double p,
- boolean useInversion, double p0, int begin, int end) {
-
- // Assign probability values as 30-bit integers
- final int size = end - begin + 1;
- final int[] prob = new int[size];
- double t = p0;
- final double h = p / (1 - p);
- for (int i = 1; i <= begin; i++) {
- t *= (trials + 1 - i) * h / i;
- }
- int sum = toUnsignedInt30(t);
- prob[0] = sum;
- for (int i = begin + 1; i <= end; i++) {
- t *= (trials + 1 - i) * h / i;
- prob[i - begin] = toUnsignedInt30(t);
- sum += prob[i - begin];
+ return createBinomialDistributionSamplerFromRange(rng, trials, p, useInversion,
+ p0, begin, end);
}
- // If the sum is < 2^30 add the remaining sum to the mode (floor((n+1)p))).
- // If above 2^30 then the effect is truncation of the long tail of the distribution.
- final int mode = (int) ((trials + 1) * p) - begin;
- prob[mode] += Math.max(0, INT_30 - sum);
+ /**
+ * Creates the Binomial distribution sampler using only the probability values for {@code X}
+ * between the begin and the end (inclusive).
+ *
+ * @param rng Generator of uniformly distributed random numbers.
+ * @param trials Number of trials.
+ * @param p Probability of success (p).
+ * @param useInversion Set to {@code true} if the probability was inverted.
+ * @param p0 Probability at {@code X=0}
+ * @param begin Begin value {@code X} for the distribution.
+ * @param end End value {@code X} for the distribution.
+ * @return Sampler.
+ */
+ private static SharedStateDiscreteSampler createBinomialDistributionSamplerFromRange(
+ UniformRandomProvider rng, int trials, double p,
+ boolean useInversion, double p0, int begin, int end) {
+
+ // Assign probability values as 30-bit integers
+ final int size = end - begin + 1;
+ final int[] prob = new int[size];
+ double t = p0;
+ final double h = p / (1 - p);
+ for (int i = 1; i <= begin; i++) {
+ t *= (trials + 1 - i) * h / i;
+ }
+ int sum = toUnsignedInt30(t);
+ prob[0] = sum;
+ for (int i = begin + 1; i <= end; i++) {
+ t *= (trials + 1 - i) * h / i;
+ prob[i - begin] = toUnsignedInt30(t);
+ sum += prob[i - begin];
+ }
- final MarsagliaTsangWangDiscreteSampler sampler = createSampler(rng, BINOMIAL_NAME, prob, begin);
+ // If the sum is < 2^30 add the remaining sum to the mode (floor((n+1)p))).
+ // If above 2^30 then the effect is truncation of the long tail of the distribution.
+ final int mode = (int) ((trials + 1) * p) - begin;
+ prob[mode] += Math.max(0, INT_30 - sum);
- // Check if an inversion was made
- return useInversion ?
- new MarsagliaTsangWangInversionBinomialSampler(trials, sampler) :
- sampler;
- }
+ final SharedStateDiscreteSampler sampler = createSampler(rng, BINOMIAL_NAME, prob, begin);
- /**
- * Convert the probability to an integer in the range [0,2^30]. This is the numerator of
- * a fraction with assumed denominator 2<sup>30</sup>.
- *
- * @param p Probability.
- * @return the fraction numerator
- */
- private static int toUnsignedInt30(double p) {
- return (int) (p * INT_30 + 0.5);
+ // Check if an inversion was made
+ return useInversion ?
+ new MarsagliaTsangWangInversionBinomialSampler(trials, sampler) :
+ sampler;
+ }
}
}
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/AliasMethodDiscreteSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/AliasMethodDiscreteSamplerTest.java
index e5f9108..0264c7c 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/AliasMethodDiscreteSamplerTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/AliasMethodDiscreteSamplerTest.java
@@ -68,7 +68,7 @@ public class AliasMethodDiscreteSamplerTest {
@Test
public void testToString() {
- final AliasMethodDiscreteSampler sampler = createSampler(new double[] {0.5, 0.5});
+ final SharedStateDiscreteSampler sampler = createSampler(new double[] {0.5, 0.5});
Assert.assertTrue(sampler.toString().toLowerCase().contains("alias method"));
}
@@ -78,9 +78,9 @@ public class AliasMethodDiscreteSamplerTest {
* @param probabilities the probabilities
* @return the alias method discrete sampler
*/
- private static AliasMethodDiscreteSampler createSampler(double[] probabilities) {
+ private static SharedStateDiscreteSampler createSampler(double[] probabilities) {
final UniformRandomProvider rng = RandomSource.create(RandomSource.SPLIT_MIX_64);
- return AliasMethodDiscreteSampler.create(rng, probabilities, -1);
+ return AliasMethodDiscreteSampler.of(rng, probabilities, -1);
}
/**
@@ -130,7 +130,7 @@ public class AliasMethodDiscreteSamplerTest {
@Test
public void testNonUniformSamplesWithProbabilitiesWithDefaultFactoryConstructor() {
final double[] expected = {0.1, 0.2, 0.3, 0.1, 0.3 };
- checkSamples(AliasMethodDiscreteSampler.create(RandomSource.create(RandomSource.SPLIT_MIX_64), expected), expected);
+ checkSamples(AliasMethodDiscreteSampler.of(RandomSource.create(RandomSource.SPLIT_MIX_64), expected), expected);
}
/**
@@ -218,7 +218,7 @@ public class AliasMethodDiscreteSamplerTest {
*
* @param expected the expected probabilities
*/
- private static void checkSamples(AliasMethodDiscreteSampler sampler, double[] probabilies) {
+ private static void checkSamples(SharedStateDiscreteSampler sampler, double[] probabilies) {
final int numberOfSamples = 10000;
final long[] samples = new long[probabilies.length];
for (int i = 0; i < numberOfSamples; i++) {
@@ -275,8 +275,8 @@ public class AliasMethodDiscreteSamplerTest {
final UniformRandomProvider rng1 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
final UniformRandomProvider rng2 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
// Use negative alpha to disable padding
- final AliasMethodDiscreteSampler sampler1 =
- AliasMethodDiscreteSampler.create(rng1, probabilities, -1);
+ final SharedStateDiscreteSampler sampler1 =
+ AliasMethodDiscreteSampler.of(rng1, probabilities, -1);
final SharedStateDiscreteSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
RandomAssert.assertProduceSameSequence(sampler1, sampler2);
}
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/DiscreteSamplersList.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/DiscreteSamplersList.java
index f6fe40e..73c7cb7 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/DiscreteSamplersList.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/DiscreteSamplersList.java
@@ -53,12 +53,12 @@ public final class DiscreteSamplersList {
add(LIST, new org.apache.commons.math3.distribution.BinomialDistribution(unusedRng, trialsBinomial, probSuccessBinomial),
// range [9,16]
MathArrays.sequence(8, 9, 1),
- MarsagliaTsangWangDiscreteSampler.createBinomialDistribution(RandomSource.create(RandomSource.WELL_19937_A), trialsBinomial, probSuccessBinomial));
+ MarsagliaTsangWangDiscreteSampler.Binomial.of(RandomSource.create(RandomSource.WELL_19937_A), trialsBinomial, probSuccessBinomial));
// Inverted
add(LIST, new org.apache.commons.math3.distribution.BinomialDistribution(unusedRng, trialsBinomial, 1 - probSuccessBinomial),
// range [4,11] = [20-16, 20-9]
MathArrays.sequence(8, 4, 1),
- MarsagliaTsangWangDiscreteSampler.createBinomialDistribution(RandomSource.create(RandomSource.WELL_19937_C), trialsBinomial, 1 - probSuccessBinomial));
+ MarsagliaTsangWangDiscreteSampler.Binomial.of(RandomSource.create(RandomSource.WELL_19937_C), trialsBinomial, 1 - probSuccessBinomial));
// Geometric ("inverse method").
final double probSuccessGeometric = 0.21;
@@ -68,7 +68,7 @@ public final class DiscreteSamplersList {
// Geometric.
add(LIST, new org.apache.commons.math3.distribution.GeometricDistribution(unusedRng, probSuccessGeometric),
MathArrays.sequence(10, 0, 1),
- new GeometricSampler(RandomSource.create(RandomSource.XOR_SHIFT_1024_S), probSuccessGeometric));
+ GeometricSampler.of(RandomSource.create(RandomSource.XOR_SHIFT_1024_S), probSuccessGeometric));
// Hypergeometric ("inverse method").
final int popSizeHyper = 34;
@@ -136,10 +136,10 @@ public final class DiscreteSamplersList {
new SmallMeanPoissonSampler(RandomSource.create(RandomSource.XO_SHI_RO_256_PLUS), meanPoisson));
add(LIST, new org.apache.commons.math3.distribution.PoissonDistribution(unusedRng, meanPoisson, epsilonPoisson, maxIterationsPoisson),
MathArrays.sequence(10, 0, 1),
- new KempSmallMeanPoissonSampler(RandomSource.create(RandomSource.XO_SHI_RO_128_PLUS), meanPoisson));
+ KempSmallMeanPoissonSampler.of(RandomSource.create(RandomSource.XO_SHI_RO_128_PLUS), meanPoisson));
add(LIST, new org.apache.commons.math3.distribution.PoissonDistribution(unusedRng, meanPoisson, epsilonPoisson, maxIterationsPoisson),
MathArrays.sequence(10, 0, 1),
- MarsagliaTsangWangDiscreteSampler.createPoissonDistribution(RandomSource.create(RandomSource.XO_SHI_RO_128_PLUS), meanPoisson));
+ MarsagliaTsangWangDiscreteSampler.Poisson.of(RandomSource.create(RandomSource.XO_SHI_RO_128_PLUS), meanPoisson));
// Poisson (40 < mean < 80).
final double largeMeanPoisson = 67.89;
add(LIST, new org.apache.commons.math3.distribution.PoissonDistribution(unusedRng, largeMeanPoisson, epsilonPoisson, maxIterationsPoisson),
@@ -151,7 +151,7 @@ public final class DiscreteSamplersList {
new LargeMeanPoissonSampler(RandomSource.create(RandomSource.SPLIT_MIX_64), largeMeanPoisson));
add(LIST, new org.apache.commons.math3.distribution.PoissonDistribution(unusedRng, largeMeanPoisson, epsilonPoisson, maxIterationsPoisson),
MathArrays.sequence(50, (int) (largeMeanPoisson - 25), 1),
- MarsagliaTsangWangDiscreteSampler.createPoissonDistribution(RandomSource.create(RandomSource.XO_RO_SHI_RO_128_PLUS), largeMeanPoisson));
+ MarsagliaTsangWangDiscreteSampler.Poisson.of(RandomSource.create(RandomSource.XO_RO_SHI_RO_128_PLUS), largeMeanPoisson));
// Poisson (mean >> 40).
final double veryLargeMeanPoisson = 543.21;
add(LIST, new org.apache.commons.math3.distribution.PoissonDistribution(unusedRng, veryLargeMeanPoisson, epsilonPoisson, maxIterationsPoisson),
@@ -163,16 +163,16 @@ public final class DiscreteSamplersList {
new LargeMeanPoissonSampler(RandomSource.create(RandomSource.SPLIT_MIX_64), veryLargeMeanPoisson));
add(LIST, new org.apache.commons.math3.distribution.PoissonDistribution(unusedRng, veryLargeMeanPoisson, epsilonPoisson, maxIterationsPoisson),
MathArrays.sequence(100, (int) (veryLargeMeanPoisson - 50), 1),
- MarsagliaTsangWangDiscreteSampler.createPoissonDistribution(RandomSource.create(RandomSource.XO_RO_SHI_RO_64_SS), veryLargeMeanPoisson));
+ MarsagliaTsangWangDiscreteSampler.Poisson.of(RandomSource.create(RandomSource.XO_RO_SHI_RO_64_SS), veryLargeMeanPoisson));
// Any discrete distribution
final double[] discreteProbabilities = new double[] {0.1, 0.2, 0.3, 0.4, 0.5};
add(LIST, discreteProbabilities,
- MarsagliaTsangWangDiscreteSampler.createDiscreteDistribution(RandomSource.create(RandomSource.XO_SHI_RO_512_PLUS), discreteProbabilities));
+ MarsagliaTsangWangDiscreteSampler.Enumerated.of(RandomSource.create(RandomSource.XO_SHI_RO_512_PLUS), discreteProbabilities));
add(LIST, discreteProbabilities,
- new GuideTableDiscreteSampler(RandomSource.create(RandomSource.XO_SHI_RO_512_SS), discreteProbabilities));
+ GuideTableDiscreteSampler.of(RandomSource.create(RandomSource.XO_SHI_RO_512_SS), discreteProbabilities));
add(LIST, discreteProbabilities,
- AliasMethodDiscreteSampler.create(RandomSource.create(RandomSource.KISS), discreteProbabilities));
+ AliasMethodDiscreteSampler.of(RandomSource.create(RandomSource.KISS), discreteProbabilities));
} catch (Exception e) {
// CHECKSTYLE: stop Regexp
System.err.println("Unexpected exception while creating the list of samplers: " + e);
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/GeometricSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/GeometricSamplerTest.java
index f384485..baaa77f 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/GeometricSamplerTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/GeometricSamplerTest.java
@@ -33,7 +33,7 @@ public class GeometricSamplerTest {
@Test
public void testProbabilityOfSuccessIsOneGeneratesZeroForSamples() {
final UniformRandomProvider rng = RandomSource.create(RandomSource.SPLIT_MIX_64);
- final GeometricSampler sampler = new GeometricSampler(rng, 1);
+ final SharedStateDiscreteSampler sampler = GeometricSampler.of(rng, 1);
// All samples should be 0
for (int i = 0; i < 10; i++) {
Assert.assertEquals("p=1 should have 0 for all samples", 0, sampler.sample());
@@ -56,7 +56,7 @@ public class GeometricSamplerTest {
// The internal exponential sampler validates the mean so demonstrate creating a
// geometric sampler does not throw.
final UniformRandomProvider rng = RandomSource.create(RandomSource.SPLIT_MIX_64);
- new GeometricSampler(rng, probabilityOfSuccess);
+ GeometricSampler.of(rng, probabilityOfSuccess);
}
/**
@@ -66,7 +66,7 @@ public class GeometricSamplerTest {
@Test
public void testProbabilityOfSuccessIsOneSamplerToString() {
final UniformRandomProvider unusedRng = RandomSource.create(RandomSource.SPLIT_MIX_64);
- final GeometricSampler sampler = new GeometricSampler(unusedRng, 1);
+ final SharedStateDiscreteSampler sampler = GeometricSampler.of(unusedRng, 1);
Assert.assertTrue("Missing 'Geometric' from toString",
sampler.toString().contains("Geometric"));
}
@@ -83,7 +83,7 @@ public class GeometricSamplerTest {
@Test
public void testProbabilityOfSuccessIsAlmostZeroGeneratesMaxValueForSamples() {
final UniformRandomProvider rng = RandomSource.create(RandomSource.SPLIT_MIX_64);
- final GeometricSampler sampler = new GeometricSampler(rng, Double.MIN_VALUE);
+ final SharedStateDiscreteSampler sampler = GeometricSampler.of(rng, Double.MIN_VALUE);
// All samples should be max value
for (int i = 0; i < 10; i++) {
Assert.assertEquals("p=(almost 0) should have Integer.MAX_VALUE for all samples",
@@ -98,8 +98,7 @@ public class GeometricSamplerTest {
public void testProbabilityOfSuccessAboveOneThrows() {
final UniformRandomProvider unusedRng = RandomSource.create(RandomSource.SPLIT_MIX_64);
final double probabilityOfSuccess = Math.nextUp(1.0);
- @SuppressWarnings("unused")
- final GeometricSampler sampler = new GeometricSampler(unusedRng, probabilityOfSuccess);
+ GeometricSampler.of(unusedRng, probabilityOfSuccess);
}
/**
@@ -109,8 +108,7 @@ public class GeometricSamplerTest {
public void testProbabilityOfSuccessIsZeroThrows() {
final UniformRandomProvider unusedRng = RandomSource.create(RandomSource.SPLIT_MIX_64);
final double probabilityOfSuccess = 0;
- @SuppressWarnings("unused")
- final GeometricSampler sampler = new GeometricSampler(unusedRng, probabilityOfSuccess);
+ GeometricSampler.of(unusedRng, probabilityOfSuccess);
}
/**
@@ -138,8 +136,8 @@ public class GeometricSamplerTest {
private static void testSharedStateSampler(double probabilityOfSuccess) {
final UniformRandomProvider rng1 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
final UniformRandomProvider rng2 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
- final GeometricSampler sampler1 =
- new GeometricSampler(rng1, probabilityOfSuccess);
+ final SharedStateDiscreteSampler sampler1 =
+ GeometricSampler.of(rng1, probabilityOfSuccess);
final SharedStateDiscreteSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
RandomAssert.assertProduceSameSequence(sampler1, sampler2);
}
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/GuideTableDiscreteSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/GuideTableDiscreteSamplerTest.java
index e38b3db..fc29e0e 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/GuideTableDiscreteSamplerTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/GuideTableDiscreteSamplerTest.java
@@ -76,7 +76,7 @@ public class GuideTableDiscreteSamplerTest {
@Test
public void testToString() {
- final GuideTableDiscreteSampler sampler = createSampler(new double[] {0.5, 0.5}, 1.0);
+ final SharedStateDiscreteSampler sampler = createSampler(new double[] {0.5, 0.5}, 1.0);
Assert.assertTrue(sampler.toString().toLowerCase().contains("guide table"));
}
@@ -86,9 +86,9 @@ public class GuideTableDiscreteSamplerTest {
* @param probabilities the probabilities
* @return the alias method discrete sampler
*/
- private static GuideTableDiscreteSampler createSampler(double[] probabilities, double alpha) {
+ private static SharedStateDiscreteSampler createSampler(double[] probabilities, double alpha) {
final UniformRandomProvider rng = RandomSource.create(RandomSource.SPLIT_MIX_64);
- return new GuideTableDiscreteSampler(rng, probabilities, alpha);
+ return GuideTableDiscreteSampler.of(rng, probabilities, alpha);
}
/**
@@ -201,7 +201,7 @@ public class GuideTableDiscreteSamplerTest {
* @param alpha the alpha
*/
private static void checkSamples(double[] probabilies, double alpha) {
- final GuideTableDiscreteSampler sampler = createSampler(probabilies, alpha);
+ final SharedStateDiscreteSampler sampler = createSampler(probabilies, alpha);
final int numberOfSamples = 10000;
final long[] samples = new long[probabilies.length];
@@ -244,8 +244,8 @@ public class GuideTableDiscreteSamplerTest {
final UniformRandomProvider rng1 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
final UniformRandomProvider rng2 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
final double[] probabilities = {0.1, 0, 0.2, 0.3, 0.1, 0.3, 0};
- final GuideTableDiscreteSampler sampler1 =
- new GuideTableDiscreteSampler(rng1, probabilities);
+ final SharedStateDiscreteSampler sampler1 =
+ GuideTableDiscreteSampler.of(rng1, probabilities);
final SharedStateDiscreteSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
RandomAssert.assertProduceSameSequence(sampler1, sampler2);
}
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/KempSmallMeanPoissonSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/KempSmallMeanPoissonSamplerTest.java
index 2796ff0..cc0b7c8 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/KempSmallMeanPoissonSamplerTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/KempSmallMeanPoissonSamplerTest.java
@@ -46,7 +46,7 @@ public class KempSmallMeanPoissonSamplerTest {
public void testConstructorThrowsWithMeanLargerThanUpperBound() {
final double mean = SUPPORTED_UPPER_BOUND + 1;
@SuppressWarnings("unused")
- KempSmallMeanPoissonSampler sampler = new KempSmallMeanPoissonSampler(dummyRng, mean);
+ SharedStateDiscreteSampler sampler = KempSmallMeanPoissonSampler.of(dummyRng, mean);
}
/**
@@ -56,7 +56,7 @@ public class KempSmallMeanPoissonSamplerTest {
public void testConstructorThrowsWithZeroMean() {
final double mean = 0;
@SuppressWarnings("unused")
- KempSmallMeanPoissonSampler sampler = new KempSmallMeanPoissonSampler(dummyRng, mean);
+ SharedStateDiscreteSampler sampler = KempSmallMeanPoissonSampler.of(dummyRng, mean);
}
/**
@@ -66,7 +66,7 @@ public class KempSmallMeanPoissonSamplerTest {
public void testConstructorThrowsWithNegativeMean() {
final double mean = -1;
@SuppressWarnings("unused")
- KempSmallMeanPoissonSampler sampler = new KempSmallMeanPoissonSampler(dummyRng, mean);
+ SharedStateDiscreteSampler sampler = KempSmallMeanPoissonSampler.of(dummyRng, mean);
}
/**
@@ -76,7 +76,7 @@ public class KempSmallMeanPoissonSamplerTest {
public void testConstructorWithNaNMean() {
final double mean = Double.NaN;
@SuppressWarnings("unused")
- KempSmallMeanPoissonSampler sampler = new KempSmallMeanPoissonSampler(dummyRng, mean);
+ SharedStateDiscreteSampler sampler = KempSmallMeanPoissonSampler.of(dummyRng, mean);
}
/**
@@ -129,7 +129,7 @@ public class KempSmallMeanPoissonSamplerTest {
PoissonDistribution.DEFAULT_EPSILON, PoissonDistribution.DEFAULT_MAX_ITERATIONS);
final FixedRNG rng = new FixedRNG(0);
- final KempSmallMeanPoissonSampler sampler = new KempSmallMeanPoissonSampler(rng, mean);
+ final SharedStateDiscreteSampler sampler = KempSmallMeanPoissonSampler.of(rng, mean);
// Lower bound should be zero
testSample(rng, sampler, 0, 0, 0);
@@ -154,8 +154,8 @@ public class KempSmallMeanPoissonSamplerTest {
final UniformRandomProvider rng1 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
final UniformRandomProvider rng2 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
final double mean = 1.23;
- final KempSmallMeanPoissonSampler sampler1 =
- new KempSmallMeanPoissonSampler(rng1, mean);
+ final SharedStateDiscreteSampler sampler1 =
+ KempSmallMeanPoissonSampler.of(rng1, mean);
final SharedStateDiscreteSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
RandomAssert.assertProduceSameSequence(sampler1, sampler2);
}
@@ -169,7 +169,7 @@ public class KempSmallMeanPoissonSamplerTest {
* @param lower the expected lower limit
* @param upper the expected upper limit
*/
- private static void testSample(FixedRNG rng, KempSmallMeanPoissonSampler sampler, double cumulativeProbability,
+ private static void testSample(FixedRNG rng, SharedStateDiscreteSampler sampler, double cumulativeProbability,
int lower, int upper) {
rng.setValue(cumulativeProbability);
final int sample = sampler.sample();
diff --git a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/MarsagliaTsangWangDiscreteSamplerTest.java b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/MarsagliaTsangWangDiscreteSamplerTest.java
index 5f2dd87..4b05b1e 100644
--- a/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/MarsagliaTsangWangDiscreteSamplerTest.java
+++ b/commons-rng-sampling/src/test/java/org/apache/commons/rng/sampling/distribution/MarsagliaTsangWangDiscreteSamplerTest.java
@@ -85,9 +85,9 @@ public class MarsagliaTsangWangDiscreteSamplerTest {
* @param probabilities Probabilities.
* @return the sampler
*/
- private static MarsagliaTsangWangDiscreteSampler createDiscreteDistributionSampler(double[] probabilities) {
+ private static SharedStateDiscreteSampler createDiscreteDistributionSampler(double[] probabilities) {
final UniformRandomProvider rng = new SplitMix64(0L);
- return MarsagliaTsangWangDiscreteSampler.createDiscreteDistribution(rng, probabilities);
+ return MarsagliaTsangWangDiscreteSampler.Enumerated.of(rng, probabilities);
}
// Sampling tests
@@ -146,9 +146,9 @@ public class MarsagliaTsangWangDiscreteSamplerTest {
final double[] p2 = createProbabilities(offset2, prob);
final double[] p3 = createProbabilities(offset3, prob);
- final MarsagliaTsangWangDiscreteSampler sampler1 = MarsagliaTsangWangDiscreteSampler.createDiscreteDistribution(rng1, p1);
- final MarsagliaTsangWangDiscreteSampler sampler2 = MarsagliaTsangWangDiscreteSampler.createDiscreteDistribution(rng2, p2);
- final MarsagliaTsangWangDiscreteSampler sampler3 = MarsagliaTsangWangDiscreteSampler.createDiscreteDistribution(rng3, p3);
+ final SharedStateDiscreteSampler sampler1 = MarsagliaTsangWangDiscreteSampler.Enumerated.of(rng1, p1);
+ final SharedStateDiscreteSampler sampler2 = MarsagliaTsangWangDiscreteSampler.Enumerated.of(rng2, p2);
+ final SharedStateDiscreteSampler sampler3 = MarsagliaTsangWangDiscreteSampler.Enumerated.of(rng3, p3);
for (int i = 0; i < values.length; i++) {
// Remove offsets
@@ -189,12 +189,12 @@ public class MarsagliaTsangWangDiscreteSamplerTest {
// First test the table is completely filled to 2^30
final UniformRandomProvider dummyRng = new FixedSequenceIntProvider(new int[] {0xffffffff});
- final MarsagliaTsangWangDiscreteSampler dummySampler = MarsagliaTsangWangDiscreteSampler.createDiscreteDistribution(dummyRng, probabilities);
+ final SharedStateDiscreteSampler dummySampler = MarsagliaTsangWangDiscreteSampler.Enumerated.of(dummyRng, probabilities);
// This will throw if the table is incomplete as it hits the upper limit
dummySampler.sample();
// Do a test of the actual sampler
- final MarsagliaTsangWangDiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.createDiscreteDistribution(rng, probabilities);
+ final SharedStateDiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.Enumerated.of(rng, probabilities);
final int numberOfSamples = 10000;
final long[] samples = new long[probabilities.length];
@@ -300,7 +300,7 @@ public class MarsagliaTsangWangDiscreteSamplerTest {
final UniformRandomProvider rng = new FixedRNG();
final double mean = 1025;
@SuppressWarnings("unused")
- final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.createPoissonDistribution(rng, mean);
+ final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.Poisson.of(rng, mean);
}
/**
@@ -311,7 +311,7 @@ public class MarsagliaTsangWangDiscreteSamplerTest {
final UniformRandomProvider rng = new FixedRNG();
final double mean = 0;
@SuppressWarnings("unused")
- final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.createPoissonDistribution(rng, mean);
+ final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.Poisson.of(rng, mean);
}
/**
@@ -322,7 +322,7 @@ public class MarsagliaTsangWangDiscreteSamplerTest {
final UniformRandomProvider rng = new FixedRNG();
final double mean = 1024;
@SuppressWarnings("unused")
- final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.createPoissonDistribution(rng, mean);
+ final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.Poisson.of(rng, mean);
}
/**
@@ -333,7 +333,7 @@ public class MarsagliaTsangWangDiscreteSamplerTest {
public void testCreatePoissonDistributionWithSmallMean() {
final UniformRandomProvider rng = new FixedRNG();
final double mean = 0.25;
- final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.createPoissonDistribution(rng, mean);
+ final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.Poisson.of(rng, mean);
// This will throw if the table does not sum to 2^30
sampler.sample();
}
@@ -347,7 +347,7 @@ public class MarsagliaTsangWangDiscreteSamplerTest {
public void testCreatePoissonDistributionWithMediumMean() {
final UniformRandomProvider rng = new FixedRNG();
final double mean = 21.4;
- final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.createPoissonDistribution(rng, mean);
+ final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.Poisson.of(rng, mean);
// This will throw if the table does not sum to 2^30
sampler.sample();
}
@@ -361,7 +361,7 @@ public class MarsagliaTsangWangDiscreteSamplerTest {
final int trials = -1;
final double p = 0.5;
@SuppressWarnings("unused")
- final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.createBinomialDistribution(rng, trials, p);
+ final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.Binomial.of(rng, trials, p);
}
/**
@@ -373,7 +373,7 @@ public class MarsagliaTsangWangDiscreteSamplerTest {
final int trials = 1 << 16; // 2^16
final double p = 0.5;
@SuppressWarnings("unused")
- final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.createBinomialDistribution(rng, trials, p);
+ final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.Binomial.of(rng, trials, p);
}
/**
@@ -385,7 +385,7 @@ public class MarsagliaTsangWangDiscreteSamplerTest {
final int trials = 1;
final double p = -0.5;
@SuppressWarnings("unused")
- final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.createBinomialDistribution(rng, trials, p);
+ final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.Binomial.of(rng, trials, p);
}
/**
@@ -397,7 +397,7 @@ public class MarsagliaTsangWangDiscreteSamplerTest {
final int trials = 1;
final double p = 1.5;
@SuppressWarnings("unused")
- final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.createBinomialDistribution(rng, trials, p);
+ final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.Binomial.of(rng, trials, p);
}
/**
@@ -422,7 +422,7 @@ public class MarsagliaTsangWangDiscreteSamplerTest {
Assert.assertEquals("Invalid test set-up for p(0)", 0, getBinomialP0(trials + 1, p), 0);
// This will throw if the table does not sum to 2^30
- final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.createBinomialDistribution(rng, trials, p);
+ final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.Binomial.of(rng, trials, p);
sampler.sample();
}
@@ -439,7 +439,7 @@ public class MarsagliaTsangWangDiscreteSamplerTest {
// Validate set-up
Assert.assertEquals("Invalid test set-up for p(0)", 0, getBinomialP0(trials, p), 0);
@SuppressWarnings("unused")
- final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.createBinomialDistribution(rng, trials, p);
+ final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.Binomial.of(rng, trials, p);
}
/**
@@ -482,7 +482,7 @@ public class MarsagliaTsangWangDiscreteSamplerTest {
Assert.assertEquals("Invalid test set-up for p(0)", Double.MIN_VALUE, getBinomialP0(trials, p), 0);
Assert.assertEquals("Invalid test set-up for p(0)", 0, getBinomialP0(trials, Math.nextAfter(p, 1)), 0);
- final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.createBinomialDistribution(rng, trials, p);
+ final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.Binomial.of(rng, trials, p);
// This will throw if the table does not sum to 2^30
sampler.sample();
}
@@ -506,7 +506,7 @@ public class MarsagliaTsangWangDiscreteSamplerTest {
final UniformRandomProvider rng = new FixedRNG();
final int trials = 1000000;
final double p = 0;
- final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.createBinomialDistribution(rng, trials, p);
+ final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.Binomial.of(rng, trials, p);
for (int i = 0; i < 5; i++) {
Assert.assertEquals(0, sampler.sample());
}
@@ -523,7 +523,7 @@ public class MarsagliaTsangWangDiscreteSamplerTest {
final UniformRandomProvider rng = new FixedRNG();
final int trials = 1000000;
final double p = 1;
- final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.createBinomialDistribution(rng, trials, p);
+ final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.Binomial.of(rng, trials, p);
for (int i = 0; i < 5; i++) {
Assert.assertEquals(trials, sampler.sample());
}
@@ -541,7 +541,7 @@ public class MarsagliaTsangWangDiscreteSamplerTest {
final UniformRandomProvider rng = new FixedRNG();
final int trials = 65000;
final double p = 0.01;
- final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.createBinomialDistribution(rng, trials, p);
+ final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.Binomial.of(rng, trials, p);
// This will throw if the table does not sum to 2^30
sampler.sample();
}
@@ -555,7 +555,7 @@ public class MarsagliaTsangWangDiscreteSamplerTest {
final UniformRandomProvider rng = new FixedRNG();
final int trials = 10;
final double p = 0.5;
- final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.createBinomialDistribution(rng, trials, p);
+ final DiscreteSampler sampler = MarsagliaTsangWangDiscreteSampler.Binomial.of(rng, trials, p);
// This will throw if the table does not sum to 2^30
sampler.sample();
}
@@ -570,8 +570,8 @@ public class MarsagliaTsangWangDiscreteSamplerTest {
final int trials = 10;
final double p1 = 0.4;
final double p2 = 1 - p1;
- final DiscreteSampler sampler1 = MarsagliaTsangWangDiscreteSampler.createBinomialDistribution(rng, trials, p1);
- final DiscreteSampler sampler2 = MarsagliaTsangWangDiscreteSampler.createBinomialDistribution(rng, trials, p2);
+ final DiscreteSampler sampler1 = MarsagliaTsangWangDiscreteSampler.Binomial.of(rng, trials, p1);
+ final DiscreteSampler sampler2 = MarsagliaTsangWangDiscreteSampler.Binomial.of(rng, trials, p2);
Assert.assertEquals(sampler1.toString(), sampler2.toString());
}
@@ -610,8 +610,8 @@ public class MarsagliaTsangWangDiscreteSamplerTest {
final UniformRandomProvider rng1 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
final UniformRandomProvider rng2 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
double[] probabilities = createProbabilities(offset, prob);
- final MarsagliaTsangWangDiscreteSampler sampler1 =
- MarsagliaTsangWangDiscreteSampler.createDiscreteDistribution(rng1, probabilities);
+ final SharedStateDiscreteSampler sampler1 =
+ MarsagliaTsangWangDiscreteSampler.Enumerated.of(rng1, probabilities);
final SharedStateDiscreteSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
RandomAssert.assertProduceSameSequence(sampler1, sampler2);
}
@@ -643,8 +643,8 @@ public class MarsagliaTsangWangDiscreteSamplerTest {
private static void testSharedStateSampler(int trials, double probabilityOfSuccess) {
final UniformRandomProvider rng1 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
final UniformRandomProvider rng2 = RandomSource.create(RandomSource.SPLIT_MIX_64, 0L);
- final MarsagliaTsangWangDiscreteSampler sampler1 =
- MarsagliaTsangWangDiscreteSampler.createBinomialDistribution(rng1, trials, probabilityOfSuccess);
+ final SharedStateDiscreteSampler sampler1 =
+ MarsagliaTsangWangDiscreteSampler.Binomial.of(rng1, trials, probabilityOfSuccess);
final SharedStateDiscreteSampler sampler2 = sampler1.withUniformRandomProvider(rng2);
RandomAssert.assertProduceSameSequence(sampler1, sampler2);
}
diff --git a/src/main/resources/pmd/pmd-ruleset.xml b/src/main/resources/pmd/pmd-ruleset.xml
index 39d64db..40c1312 100644
--- a/src/main/resources/pmd/pmd-ruleset.xml
+++ b/src/main/resources/pmd/pmd-ruleset.xml
@@ -74,7 +74,8 @@
<properties>
<!-- Array is generated internally in this case. -->
<property name="violationSuppressXPath"
- value="//ClassOrInterfaceDeclaration[@Image='PoissonSamplerCache' or @Image='AliasMethodDiscreteSampler']"/>
+ value="//ClassOrInterfaceDeclaration[@Image='PoissonSamplerCache' or @Image='AliasMethodDiscreteSampler'
+ or @Image='GuideTableDiscreteSampler']"/>
</properties>
</rule>