From 4fdbd676776c4831194f2909b102a05923fc429e Mon Sep 17 00:00:00 2001 From: Phil Steitz Date: Mon, 31 May 2010 23:39:25 +0000 Subject: [PATCH] Added sampling methods to the distribution classes, based on the random data generation methods in the random package. JIRA: MATH-310 git-svn-id: https://svn.apache.org/repos/asf/commons/proper/math/trunk@949895 13f79535-47bb-0310-9956-ffa450edef68 --- .../commons/math/MessagesResources_fr.java | 4 + .../AbstractContinuousDistribution.java | 54 ++++++++- .../AbstractIntegerDistribution.java | 53 ++++++++- .../ExponentialDistributionImpl.java | 17 +++ .../distribution/NormalDistributionImpl.java | 12 ++ .../distribution/PoissonDistributionImpl.java | 22 ++++ src/site/xdoc/changes.xml | 1 + .../org/apache/commons/math/TestUtils.java | 76 +++++++++++- .../ContinuousDistributionAbstractTest.java | 17 +++ .../IntegerDistributionAbstractTest.java | 28 +++++ .../commons/math/random/RandomDataTest.java | 108 ++++-------------- 11 files changed, 306 insertions(+), 86 deletions(-) diff --git a/src/main/java/org/apache/commons/math/MessagesResources_fr.java b/src/main/java/org/apache/commons/math/MessagesResources_fr.java index 7bd848a69..1fc6de08f 100644 --- a/src/main/java/org/apache/commons/math/MessagesResources_fr.java +++ b/src/main/java/org/apache/commons/math/MessagesResources_fr.java @@ -753,6 +753,10 @@ public class MessagesResources_fr { "Discrete cumulative probability function returned NaN for argument {0}", "Discr\u00e8tes fonction de probabilit\u00e9 cumulative retourn\u00e9 NaN \u00e0 l''argument de {0}" }, + // org.apache.commons.math.distribution.AbstractIntegerDistribution + // org.apache.commons.math.distribution.AbstractContinuousDistribution + { "Sample size must be positive", + "Taille de l'\u00e9chantillon doit \u00eatre positif" }, // org.apache.commons.math.distribution.BinomialDistributionImpl { "number of trials must be non-negative ({0})", diff --git a/src/main/java/org/apache/commons/math/distribution/AbstractContinuousDistribution.java b/src/main/java/org/apache/commons/math/distribution/AbstractContinuousDistribution.java index 2eec29804..b71121358 100644 --- a/src/main/java/org/apache/commons/math/distribution/AbstractContinuousDistribution.java +++ b/src/main/java/org/apache/commons/math/distribution/AbstractContinuousDistribution.java @@ -25,6 +25,7 @@ import org.apache.commons.math.MathRuntimeException; import org.apache.commons.math.analysis.UnivariateRealFunction; import org.apache.commons.math.analysis.solvers.BrentSolver; import org.apache.commons.math.analysis.solvers.UnivariateRealSolverUtils; +import org.apache.commons.math.random.RandomDataImpl; /** * Base class for continuous distributions. Default implementations are @@ -39,6 +40,12 @@ public abstract class AbstractContinuousDistribution /** Serializable version identifier */ private static final long serialVersionUID = -38038050983108802L; + + /** + * RandomData instance used to generate samples from the distribution + * @since 2.2 + */ + protected final RandomDataImpl randomData = new RandomDataImpl(); /** * Solver absolute accuracy for inverse cum computation @@ -102,7 +109,7 @@ public abstract class AbstractContinuousDistribution } }; - // Try to bracket root, test domain endoints if this fails + // Try to bracket root, test domain endpoints if this fails double lowerBound = getDomainLowerBound(p); double upperBound = getDomainUpperBound(p); double[] bracket = null; @@ -134,6 +141,50 @@ public abstract class AbstractContinuousDistribution return root; } + /** + * Reseeds the random generator used to generate samples. + * + * @param seed the new seed + * @since 2.2 + */ + public void reseedRandomGenerator(long seed) { + randomData.reSeed(seed); + } + + /** + * Generates a random value sampled from this distribution. The default + * implementation uses the + * inversion method. + * + * @return random value + * @since 2.2 + * @throws MathException if an error occurs generating the random value + */ + public double sample() throws MathException { + return randomData.nextInversionDeviate(this); + } + + /** + * Generates a random sample from the distribution. The default implementation + * generates the sample by calling {@link #sample()} in a loop. + * + * @param sampleSize number of random values to generate + * @since 2.2 + * @return an array representing the random sample + * @throws MathException if an error occurs generating the sample + * @throws IllegalArgumentException if sampleSize is not positive + */ + public double[] sample(int sampleSize) throws MathException { + if (sampleSize <= 0) { + MathRuntimeException.createIllegalArgumentException("Sample size must be positive"); + } + double[] out = new double[sampleSize]; + for (int i = 0; i < sampleSize; i++) { + out[i] = sample(); + } + return out; + } + /** * Access the initial domain value, based on p, used to * bracket a CDF root. This method is used by @@ -175,4 +226,5 @@ public abstract class AbstractContinuousDistribution protected double getSolverAbsoluteAccuracy() { return solverAbsoluteAccuracy; } + } diff --git a/src/main/java/org/apache/commons/math/distribution/AbstractIntegerDistribution.java b/src/main/java/org/apache/commons/math/distribution/AbstractIntegerDistribution.java index 284af3864..eea53e029 100644 --- a/src/main/java/org/apache/commons/math/distribution/AbstractIntegerDistribution.java +++ b/src/main/java/org/apache/commons/math/distribution/AbstractIntegerDistribution.java @@ -21,6 +21,7 @@ import java.io.Serializable; import org.apache.commons.math.FunctionEvaluationException; import org.apache.commons.math.MathException; import org.apache.commons.math.MathRuntimeException; +import org.apache.commons.math.random.RandomDataImpl; /** @@ -44,6 +45,12 @@ public abstract class AbstractIntegerDistribution extends AbstractDistribution /** Serializable version identifier */ private static final long serialVersionUID = -1146319659338487221L; + /** + * RandomData instance used to generate samples from the distribution + * @since 2.2 + */ + protected final RandomDataImpl randomData = new RandomDataImpl(); + /** * Default constructor. */ @@ -209,7 +216,51 @@ public abstract class AbstractIntegerDistribution extends AbstractDistribution } /** - * Computes the cumulative probablity function and checks for NaN values returned. + * Reseeds the random generator used to generate samples. + * + * @param seed the new seed + * @since 2.2 + */ + public void reseedRandomGenerator(long seed) { + randomData.reSeed(seed); + } + + /** + * Generates a random value sampled from this distribution. The default + * implementation uses the + * inversion method. + * + * @return random value + * @since 2.2 + * @throws MathException if an error occurs generating the random value + */ + public int sample() throws MathException { + return randomData.nextInversionDeviate(this); + } + + /** + * Generates a random sample from the distribution. The default implementation + * generates the sample by calling {@link #sample()} in a loop. + * + * @param sampleSize number of random values to generate + * @since 2.2 + * @return an array representing the random sample + * @throws MathException if an error occurs generating the sample + * @throws IllegalArgumentException if sampleSize is not positive + */ + public int[] sample(int sampleSize) throws MathException { + if (sampleSize <= 0) { + MathRuntimeException.createIllegalArgumentException("Sample size must be positive"); + } + int[] out = new int[sampleSize]; + for (int i = 0; i < sampleSize; i++) { + out[i] = sample(); + } + return out; + } + + /** + * Computes the cumulative probability function and checks for NaN values returned. * Throws MathException if the value is NaN. Wraps and rethrows any MathException encountered * evaluating the cumulative probability function in a FunctionEvaluationException. Throws * FunctionEvaluationException of the cumulative probability function returns NaN. diff --git a/src/main/java/org/apache/commons/math/distribution/ExponentialDistributionImpl.java b/src/main/java/org/apache/commons/math/distribution/ExponentialDistributionImpl.java index 43a161ad4..1720b240b 100644 --- a/src/main/java/org/apache/commons/math/distribution/ExponentialDistributionImpl.java +++ b/src/main/java/org/apache/commons/math/distribution/ExponentialDistributionImpl.java @@ -175,6 +175,23 @@ public class ExponentialDistributionImpl extends AbstractContinuousDistribution return ret; } + /** + * Generates a random value sampled from this distribution. + * + *

Algorithm Description: Uses the Inversion + * Method to generate exponentially distributed random values from + * uniform deviates.

+ * + * @return random value + * @since 2.2 + * @throws MathException if an error occurs generating the random value + */ + @Override + public double sample() throws MathException { + return randomData.nextExponential(mean); + } + /** * Access the domain value lower bound, based on p, used to * bracket a CDF root. diff --git a/src/main/java/org/apache/commons/math/distribution/NormalDistributionImpl.java b/src/main/java/org/apache/commons/math/distribution/NormalDistributionImpl.java index 2faca5b76..3d521297f 100644 --- a/src/main/java/org/apache/commons/math/distribution/NormalDistributionImpl.java +++ b/src/main/java/org/apache/commons/math/distribution/NormalDistributionImpl.java @@ -228,6 +228,18 @@ public class NormalDistributionImpl extends AbstractContinuousDistribution return super.inverseCumulativeProbability(p); } + /** + * Generates a random value sampled from this distribution. + * + * @return random value + * @since 2.2 + * @throws MathException if an error occurs generating the random value + */ + @Override + public double sample() throws MathException { + return randomData.nextGaussian(mean, standardDeviation); + } + /** * Access the domain value lower bound, based on p, used to * bracket a CDF root. This method is used by diff --git a/src/main/java/org/apache/commons/math/distribution/PoissonDistributionImpl.java b/src/main/java/org/apache/commons/math/distribution/PoissonDistributionImpl.java index 0329f80b1..b227237d8 100644 --- a/src/main/java/org/apache/commons/math/distribution/PoissonDistributionImpl.java +++ b/src/main/java/org/apache/commons/math/distribution/PoissonDistributionImpl.java @@ -237,6 +237,28 @@ public class PoissonDistributionImpl extends AbstractIntegerDistribution return normal.cumulativeProbability(x + 0.5); } + /** + * Generates a random value sampled from this distribution. + * + *

Algorithm Description: + *

+ * + * @return random value + * @since 2.2 + * @throws MathException if an error occurs generating the random value + */ + @Override + public int sample() throws MathException { + return (int) Math.min(randomData.nextPoisson(mean), Integer.MAX_VALUE); + } + /** * Access the domain value lower bound, based on p, used to * bracket a CDF root. This method is used by diff --git a/src/site/xdoc/changes.xml b/src/site/xdoc/changes.xml index 8fd0a40b5..f63cb60b4 100644 --- a/src/site/xdoc/changes.xml +++ b/src/site/xdoc/changes.xml @@ -56,6 +56,7 @@ The type attribute can be add,update,fix,remove. Added random data generation methods to RandomDataImpl for the remaining distributions in the distributions package. Added a generic nextInversionDeviate method that takes a discrete or continuous distribution as argument and generates a random deviate from the distribution. + Also added sampling methods based on the implementations in RandomDataImpl to distributions. Fixed Levenberg-Marquardt optimizer that did not use the vectorial convergence checker. diff --git a/src/test/java/org/apache/commons/math/TestUtils.java b/src/test/java/org/apache/commons/math/TestUtils.java index 49d45ac42..7b37b8dd0 100644 --- a/src/test/java/org/apache/commons/math/TestUtils.java +++ b/src/test/java/org/apache/commons/math/TestUtils.java @@ -29,6 +29,7 @@ import junit.framework.AssertionFailedError; import org.apache.commons.math.complex.Complex; import org.apache.commons.math.complex.ComplexFormat; +import org.apache.commons.math.distribution.ContinuousDistribution; import org.apache.commons.math.linear.FieldMatrix; import org.apache.commons.math.linear.RealMatrix; import org.apache.commons.math.stat.inference.ChiSquareTest; @@ -437,5 +438,78 @@ public class TestUtils { } assertChiSquareAccept(labels, expected, observed, alpha); } - + + /** + * Asserts the null hypothesis for a ChiSquare test. Fails and dumps arguments and test + * statistics if the null hypothesis can be rejected with confidence 100 * (1 - alpha)% + * + * @param expected expected counts + * @param observed observed counts + * @param alpha significance level of the test + */ + public static void assertChiSquareAccept(double[] expected, long[] observed, double alpha) throws Exception { + String[] labels = new String[expected.length]; + for (int i = 0; i < labels.length; i++) { + labels[i] = Integer.toString(i + 1); + } + assertChiSquareAccept(labels, expected, observed, alpha); + } + + /** + * Computes the 25th, 50th and 75th percentiles of the given distribution and returns + * these values in an array. + */ + public static double[] getDistributionQuartiles(ContinuousDistribution distribution) throws Exception { + double[] quantiles = new double[3]; + quantiles[0] = distribution.inverseCumulativeProbability(0.25d); + quantiles[1] = distribution.inverseCumulativeProbability(0.5d); + quantiles[2] = distribution.inverseCumulativeProbability(0.75d); + return quantiles; + } + + /** + * Updates observed counts of values in quartiles. + * counts[0] <-> 1st quartile ... counts[3] <-> top quartile + */ + public static void updateCounts(double value, long[] counts, double[] quartiles) { + if (value < quartiles[0]) { + counts[0]++; + } else if (value > quartiles[2]) { + counts[3]++; + } else if (value > quartiles[1]) { + counts[2]++; + } else { + counts[1]++; + } + } + + /** + * Eliminates points with zero mass from densityPoints and densityValues parallel + * arrays. Returns the number of positive mass points and collapses the arrays so + * that the first elements of the input arrays represent the positive + * mass points. + */ + public static int eliminateZeroMassPoints(int[] densityPoints, double[] densityValues) { + int positiveMassCount = 0; + for (int i = 0; i < densityValues.length; i++) { + if (densityValues[i] > 0) { + positiveMassCount++; + } + } + if (positiveMassCount < densityValues.length) { + int[] newPoints = new int[positiveMassCount]; + double[] newValues = new double[positiveMassCount]; + int j = 0; + for (int i = 0; i < densityValues.length; i++) { + if (densityValues[i] > 0) { + newPoints[j] = densityPoints[i]; + newValues[j] = densityValues[i]; + j++; + } + } + System.arraycopy(newPoints,0,densityPoints,0,positiveMassCount); + System.arraycopy(newValues,0,densityValues,0,positiveMassCount); + } + return positiveMassCount; + } } diff --git a/src/test/java/org/apache/commons/math/distribution/ContinuousDistributionAbstractTest.java b/src/test/java/org/apache/commons/math/distribution/ContinuousDistributionAbstractTest.java index fda397292..cdfd61661 100644 --- a/src/test/java/org/apache/commons/math/distribution/ContinuousDistributionAbstractTest.java +++ b/src/test/java/org/apache/commons/math/distribution/ContinuousDistributionAbstractTest.java @@ -256,6 +256,23 @@ public abstract class ContinuousDistributionAbstractTest extends TestCase { // expected } } + + /** + * Test sampling + */ + public void testSampling() throws Exception { + AbstractContinuousDistribution dist = (AbstractContinuousDistribution) makeDistribution(); + final int sampleSize = 1000; + double[] sample = dist.sample(sampleSize); + double[] quartiles = TestUtils.getDistributionQuartiles(dist); + double[] expected = {250, 250, 250, 250}; + long[] counts = new long[4]; + dist.reseedRandomGenerator(1000); // Use fixed seed + for (int i = 0; i < sampleSize; i++) { + TestUtils.updateCounts(sample[i], counts, quartiles); + } + TestUtils.assertChiSquareAccept(expected, counts, 0.001); + } //------------------ Getters / Setters for test instance data ----------- /** diff --git a/src/test/java/org/apache/commons/math/distribution/IntegerDistributionAbstractTest.java b/src/test/java/org/apache/commons/math/distribution/IntegerDistributionAbstractTest.java index 68b51ea83..9eb2d036c 100644 --- a/src/test/java/org/apache/commons/math/distribution/IntegerDistributionAbstractTest.java +++ b/src/test/java/org/apache/commons/math/distribution/IntegerDistributionAbstractTest.java @@ -16,6 +16,8 @@ */ package org.apache.commons.math.distribution; +import org.apache.commons.math.TestUtils; + import junit.framework.TestCase; /** @@ -269,6 +271,32 @@ public abstract class IntegerDistributionAbstractTest extends TestCase { // expected } } + + /** + * Test sampling + */ + public void testSampling() throws Exception { + int[] densityPoints = makeDensityTestPoints(); + double[] densityValues = makeDensityTestValues(); + int sampleSize = 1000; + int length = TestUtils.eliminateZeroMassPoints(densityPoints, densityValues); + AbstractIntegerDistribution distribution = (AbstractIntegerDistribution) makeDistribution(); + double[] expectedCounts = new double[length]; + long[] observedCounts = new long[length]; + for (int i = 0; i < length; i++) { + expectedCounts[i] = sampleSize * densityValues[i]; + } + distribution.reseedRandomGenerator(1000); // Use fixed seed + int[] sample = distribution.sample(sampleSize); + for (int i = 0; i < sampleSize; i++) { + for (int j = 0; j < length; j++) { + if (sample[i] == densityPoints[j]) { + observedCounts[j]++; + } + } + } + TestUtils.assertChiSquareAccept(densityPoints, expectedCounts, observedCounts, .001); + } //------------------ Getters / Setters for test instance data ----------- /** diff --git a/src/test/java/org/apache/commons/math/random/RandomDataTest.java b/src/test/java/org/apache/commons/math/random/RandomDataTest.java index e5a7c7512..88f01d322 100644 --- a/src/test/java/org/apache/commons/math/random/RandomDataTest.java +++ b/src/test/java/org/apache/commons/math/random/RandomDataTest.java @@ -815,108 +815,80 @@ public class RandomDataTest extends RetryTestCase { } public void testNextBeta() throws Exception { - double[] quartiles = getDistributionQuartiles(new BetaDistributionImpl(2,5)); + double[] quartiles = TestUtils.getDistributionQuartiles(new BetaDistributionImpl(2,5)); long[] counts = new long[4]; randomData.reSeed(1000); for (int i = 0; i < 1000; i++) { double value = randomData.nextBeta(2, 5); - updateCounts(value, counts, quartiles); + TestUtils.updateCounts(value, counts, quartiles); } - TestUtils.assertChiSquareAccept(quartiles, expected, counts, 0.001); + TestUtils.assertChiSquareAccept(expected, counts, 0.001); } public void testNextCauchy() throws Exception { - double[] quartiles = getDistributionQuartiles(new CauchyDistributionImpl(1.2, 2.1)); + double[] quartiles = TestUtils.getDistributionQuartiles(new CauchyDistributionImpl(1.2, 2.1)); long[] counts = new long[4]; randomData.reSeed(1000); for (int i = 0; i < 1000; i++) { double value = randomData.nextCauchy(1.2, 2.1); - updateCounts(value, counts, quartiles); + TestUtils.updateCounts(value, counts, quartiles); } - TestUtils.assertChiSquareAccept(quartiles, expected, counts, 0.001); + TestUtils.assertChiSquareAccept(expected, counts, 0.001); } public void testNextChiSquare() throws Exception { - double[] quartiles = getDistributionQuartiles(new ChiSquaredDistributionImpl(12)); + double[] quartiles = TestUtils.getDistributionQuartiles(new ChiSquaredDistributionImpl(12)); long[] counts = new long[4]; randomData.reSeed(1000); for (int i = 0; i < 1000; i++) { double value = randomData.nextChiSquare(12); - updateCounts(value, counts, quartiles); + TestUtils.updateCounts(value, counts, quartiles); } - TestUtils.assertChiSquareAccept(quartiles, expected, counts, 0.001); + TestUtils.assertChiSquareAccept(expected, counts, 0.001); } public void testNextF() throws Exception { - double[] quartiles = getDistributionQuartiles(new FDistributionImpl(12, 5)); + double[] quartiles = TestUtils.getDistributionQuartiles(new FDistributionImpl(12, 5)); long[] counts = new long[4]; randomData.reSeed(1000); for (int i = 0; i < 1000; i++) { double value = randomData.nextF(12, 5); - updateCounts(value, counts, quartiles); + TestUtils.updateCounts(value, counts, quartiles); } - TestUtils.assertChiSquareAccept(quartiles, expected, counts, 0.001); + TestUtils.assertChiSquareAccept(expected, counts, 0.001); } public void testNextGamma() throws Exception { - double[] quartiles = getDistributionQuartiles(new GammaDistributionImpl(4, 2)); + double[] quartiles = TestUtils.getDistributionQuartiles(new GammaDistributionImpl(4, 2)); long[] counts = new long[4]; randomData.reSeed(1000); for (int i = 0; i < 1000; i++) { double value = randomData.nextGamma(4, 2); - updateCounts(value, counts, quartiles); + TestUtils.updateCounts(value, counts, quartiles); } - TestUtils.assertChiSquareAccept(quartiles, expected, counts, 0.001); + TestUtils.assertChiSquareAccept(expected, counts, 0.001); } public void testNextT() throws Exception { - double[] quartiles = getDistributionQuartiles(new TDistributionImpl(10)); + double[] quartiles = TestUtils.getDistributionQuartiles(new TDistributionImpl(10)); long[] counts = new long[4]; randomData.reSeed(1000); for (int i = 0; i < 1000; i++) { double value = randomData.nextT(10); - updateCounts(value, counts, quartiles); + TestUtils.updateCounts(value, counts, quartiles); } - TestUtils.assertChiSquareAccept(quartiles, expected, counts, 0.001); + TestUtils.assertChiSquareAccept(expected, counts, 0.001); } public void testNextWeibull() throws Exception { - double[] quartiles = getDistributionQuartiles(new WeibullDistributionImpl(1.2, 2.1)); + double[] quartiles = TestUtils.getDistributionQuartiles(new WeibullDistributionImpl(1.2, 2.1)); long[] counts = new long[4]; randomData.reSeed(1000); for (int i = 0; i < 1000; i++) { double value = randomData.nextWeibull(1.2, 2.1); - updateCounts(value, counts, quartiles); + TestUtils.updateCounts(value, counts, quartiles); } - TestUtils.assertChiSquareAccept(quartiles, expected, counts, 0.001); - } - - /** - * Computes the 25th, 50th and 75th percentiles of the given distribution and returns - * these values in an array. - */ - private double[] getDistributionQuartiles(ContinuousDistribution distribution) throws Exception { - double[] quantiles = new double[3]; - quantiles[0] = distribution.inverseCumulativeProbability(0.25d); - quantiles[1] = distribution.inverseCumulativeProbability(0.5d); - quantiles[2] = distribution.inverseCumulativeProbability(0.75d); - return quantiles; - } - - /** - * Updates observed counts of values in quartiles. - * counts[0] <-> 1st quartile ... counts[3] <-> top quartile - */ - private void updateCounts(double value, long[] counts, double[] quantiles) { - if (value < quantiles[0]) { - counts[0]++; - } else if (value > quantiles[2]) { - counts[3]++; - } else if (value > quantiles[1]) { - counts[2]++; - } else { - counts[1]++; - } + TestUtils.assertChiSquareAccept(expected, counts, 0.001); } public void testNextBinomial() throws Exception { @@ -924,7 +896,7 @@ public class RandomDataTest extends RetryTestCase { int[] densityPoints = testInstance.makeDensityTestPoints(); double[] densityValues = testInstance.makeDensityTestValues(); int sampleSize = 1000; - int length = eliminateZeroMassPoints(densityPoints, densityValues); + int length = TestUtils.eliminateZeroMassPoints(densityPoints, densityValues); BinomialDistributionImpl distribution = (BinomialDistributionImpl) testInstance.makeDistribution(); double[] expectedCounts = new double[length]; long[] observedCounts = new long[length]; @@ -949,7 +921,7 @@ public class RandomDataTest extends RetryTestCase { int[] densityPoints = testInstance.makeDensityTestPoints(); double[] densityValues = testInstance.makeDensityTestValues(); int sampleSize = 1000; - int length = eliminateZeroMassPoints(densityPoints, densityValues); + int length = TestUtils.eliminateZeroMassPoints(densityPoints, densityValues); HypergeometricDistributionImpl distribution = (HypergeometricDistributionImpl) testInstance.makeDistribution(); double[] expectedCounts = new double[length]; long[] observedCounts = new long[length]; @@ -974,7 +946,7 @@ public class RandomDataTest extends RetryTestCase { int[] densityPoints = testInstance.makeDensityTestPoints(); double[] densityValues = testInstance.makeDensityTestValues(); int sampleSize = 1000; - int length = eliminateZeroMassPoints(densityPoints, densityValues); + int length = TestUtils.eliminateZeroMassPoints(densityPoints, densityValues); PascalDistributionImpl distribution = (PascalDistributionImpl) testInstance.makeDistribution(); double[] expectedCounts = new double[length]; long[] observedCounts = new long[length]; @@ -998,7 +970,7 @@ public class RandomDataTest extends RetryTestCase { int[] densityPoints = testInstance.makeDensityTestPoints(); double[] densityValues = testInstance.makeDensityTestValues(); int sampleSize = 1000; - int length = eliminateZeroMassPoints(densityPoints, densityValues); + int length = TestUtils.eliminateZeroMassPoints(densityPoints, densityValues); ZipfDistributionImpl distribution = (ZipfDistributionImpl) testInstance.makeDistribution(); double[] expectedCounts = new double[length]; long[] observedCounts = new long[length]; @@ -1017,34 +989,4 @@ public class RandomDataTest extends RetryTestCase { TestUtils.assertChiSquareAccept(densityPoints, expectedCounts, observedCounts, .001); } - /** - * Eliminates points with zero mass from densityPoints and densityValues parallel - * arrays. Returns the number of positive mass points and collapses the arrays so - * that the first elements of the input arrays represent the positive - * mass points. - */ - private int eliminateZeroMassPoints(int[] densityPoints, double[] densityValues) { - int positiveMassCount = 0; - for (int i = 0; i < densityValues.length; i++) { - if (densityValues[i] > 0) { - positiveMassCount++; - } - } - if (positiveMassCount < densityValues.length) { - int[] newPoints = new int[positiveMassCount]; - double[] newValues = new double[positiveMassCount]; - int j = 0; - for (int i = 0; i < densityValues.length; i++) { - if (densityValues[i] > 0) { - newPoints[j] = densityPoints[i]; - newValues[j] = densityValues[i]; - j++; - } - } - System.arraycopy(newPoints,0,densityPoints,0,positiveMassCount); - System.arraycopy(newValues,0,densityValues,0,positiveMassCount); - } - return positiveMassCount; - } - }