1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.statistics.distribution;
19
20 import java.util.stream.IntStream;
21 import java.util.stream.Stream;
22 import org.apache.commons.numbers.core.Precision;
23 import org.apache.commons.rng.simple.RandomSource;
24 import org.junit.jupiter.api.Assertions;
25 import org.junit.jupiter.api.Test;
26 import org.junit.jupiter.params.ParameterizedTest;
27 import org.junit.jupiter.params.provider.Arguments;
28 import org.junit.jupiter.params.provider.CsvSource;
29 import org.junit.jupiter.params.provider.MethodSource;
30
31
32
33
34
35 class HypergeometricDistributionTest extends BaseDiscreteDistributionTest {
36 @Override
37 DiscreteDistribution makeDistribution(Object... parameters) {
38 final int populationSize = (Integer) parameters[0];
39 final int numberOfSuccesses = (Integer) parameters[1];
40 final int sampleSize = (Integer) parameters[2];
41 return HypergeometricDistribution.of(populationSize, numberOfSuccesses, sampleSize);
42 }
43
44 @Override
45 Object[][] makeInvalidParameters() {
46 return new Object[][] {
47 {0, 3, 5},
48 {-1, 3, 5},
49 {5, -1, 5},
50 {5, 3, -1},
51 {5, 6, 5},
52 {5, 3, 6},
53 };
54 }
55
56 @Override
57 String[] getParameterNames() {
58 return new String[] {"PopulationSize", "NumberOfSuccesses", "SampleSize"};
59 }
60
61 @Override
62 protected double getRelativeTolerance() {
63 return 5e-15;
64 }
65
66
67
68 @ParameterizedTest
69 @MethodSource
70 void testAdditionalMoments(int populationSize,
71 int numberOfSuccesses,
72 int sampleSize,
73 double mean, double variance) {
74 final HypergeometricDistribution dist = HypergeometricDistribution.of(populationSize, numberOfSuccesses, sampleSize);
75 testMoments(dist, mean, variance, DoubleTolerances.ulps(1));
76 }
77
78 static Stream<Arguments> testAdditionalMoments() {
79 return Stream.of(
80 Arguments.of(1500, 40, 100, 40d * 100d / 1500d, (100d * 40d * (1500d - 100d) * (1500d - 40d)) / (1500d * 1500d * 1499d)),
81 Arguments.of(3000, 55, 200, 55d * 200d / 3000d, (200d * 55d * (3000d - 200d) * (3000d - 55d)) / (3000d * 3000d * 2999d))
82 );
83 }
84
85 @Test
86 void testLargeValues() {
87 final int populationSize = 3456;
88 final int sampleSize = 789;
89 final int numberOfSucceses = 101;
90
91
92 final double[][] data = {
93 {0.0, 2.75646034603961e-12, 2.75646034603961e-12, 1.0},
94 {1.0, 8.55705370142386e-11, 8.83269973602783e-11, 0.999999999997244},
95 {2.0, 1.31288129219665e-9, 1.40120828955693e-9, 0.999999999911673},
96 {3.0, 1.32724172984193e-8, 1.46736255879763e-8, 0.999999998598792},
97 {4.0, 9.94501711734089e-8, 1.14123796761385e-7, 0.999999985326375},
98 {5.0, 5.89080768883643e-7, 7.03204565645028e-7, 0.999999885876203},
99 {20.0, 0.0760051397707708, 0.27349758476299, 0.802507555007781},
100 {21.0, 0.087144222047629, 0.360641806810619, 0.72650241523701},
101 {22.0, 0.0940378846881819, 0.454679691498801, 0.639358193189381},
102 {23.0, 0.0956897500614809, 0.550369441560282, 0.545320308501199},
103 {24.0, 0.0919766921922999, 0.642346133752582, 0.449630558439718},
104 {25.0, 0.083641637261095, 0.725987771013677, 0.357653866247418},
105 {96.0, 5.93849188852098e-57, 1.0, 6.01900244560712e-57},
106 {97.0, 7.96593036832547e-59, 1.0, 8.05105570861321e-59},
107 {98.0, 8.44582921934367e-61, 1.0, 8.5125340287733e-61},
108 {99.0, 6.63604297068222e-63, 1.0, 6.670480942963e-63},
109 {100.0, 3.43501099007557e-65, 1.0, 3.4437972280786e-65},
110 {101.0, 8.78623800302957e-68, 1.0, 8.78623800302957e-68},
111
112 {sampleSize + 1, 0, 1.0, 0},
113 };
114
115 testHypergeometricDistributionProbabilities(populationSize, sampleSize, numberOfSucceses, data);
116 }
117
118 private static void testHypergeometricDistributionProbabilities(int populationSize, int sampleSize,
119 int numberOfSuccesses, double[][] data) {
120 final HypergeometricDistribution dist = HypergeometricDistribution.of(populationSize, numberOfSuccesses, sampleSize);
121 for (int i = 0; i < data.length; ++i) {
122 final int x = (int)data[i][0];
123 final double pmf = data[i][1];
124 final double actualPmf = dist.probability(x);
125 TestUtils.assertRelativelyEquals(() -> "Expected equals for <" + x + "> pmf", pmf, actualPmf, 1.0e-9);
126
127 final double cdf = data[i][2];
128 final double actualCdf = dist.cumulativeProbability(x);
129 TestUtils.assertRelativelyEquals(() -> "Expected equals for <" + x + "> cdf", cdf, actualCdf, 1.0e-9);
130
131 final double cdf1 = data[i][3];
132 final double actualCdf1 = dist.survivalProbability(x - 1);
133 TestUtils.assertRelativelyEquals(() -> "Expected equals for <" + x + "> cdf1", cdf1, actualCdf1, 1.0e-9);
134 }
135 }
136
137 @Test
138 void testMoreLargeValues() {
139 final int populationSize = 26896;
140 final int sampleSize = 895;
141 final int numberOfSucceses = 55;
142 final double[][] data = {
143 {0.0, 0.155168304750504, 0.155168304750504, 1.0},
144 {1.0, 0.29437545000746, 0.449543754757964, 0.844831695249496},
145 {2.0, 0.273841321577003, 0.723385076334967, 0.550456245242036},
146 {3.0, 0.166488572570786, 0.889873648905753, 0.276614923665033},
147 {4.0, 0.0743969744713231, 0.964270623377076, 0.110126351094247},
148 {5.0, 0.0260542785784855, 0.990324901955562, 0.0357293766229237},
149 {20.0, 3.57101101678792e-16, 1.0, 3.78252101622096e-16},
150 {21.0, 2.00551638598312e-17, 1.0, 2.11509999433041e-17},
151 {22.0, 1.04317070180562e-18, 1.0, 1.09583608347287e-18},
152 {23.0, 5.03153504903308e-20, 1.0, 5.266538166725e-20},
153 {24.0, 2.2525984149695e-21, 1.0, 2.35003117691919e-21},
154 {25.0, 9.3677424515947e-23, 1.0, 9.74327619496943e-23},
155 {50.0, 9.83633962945521e-69, 1.0, 9.8677629437617e-69},
156 {51.0, 3.13448949497553e-71, 1.0, 3.14233143064882e-71},
157 {52.0, 7.82755221928122e-74, 1.0, 7.84193567329055e-74},
158 {53.0, 1.43662126065532e-76, 1.0, 1.43834540093295e-76},
159 {54.0, 1.72312692517348e-79, 1.0, 1.7241402776278e-79},
160 {55.0, 1.01335245432581e-82, 1.0, 1.01335245432581e-82},
161 };
162 testHypergeometricDistributionProbabilities(populationSize, sampleSize, numberOfSucceses, data);
163 }
164
165
166
167
168
169
170
171 @Test
172 void testMath644() {
173 final int N = 14761461;
174 final int m = 1035;
175 final int n = 1841;
176
177 final int k = 0;
178 final HypergeometricDistribution dist = HypergeometricDistribution.of(N, m, n);
179
180
181 Assertions.assertEquals(0, Precision.compareTo(1.0, dist.survivalProbability(k - 1), 1));
182 Assertions.assertTrue(Precision.compareTo(dist.cumulativeProbability(k), 0.0, 1) > 0);
183
184
185 final double upper = 1.0 - dist.cumulativeProbability(k) + dist.probability(k);
186 Assertions.assertEquals(0, Precision.compareTo(1.0, upper, 1));
187 }
188
189 @Test
190 void testZeroTrials() {
191 final int n = 11;
192 final int m = 4;
193 final int s = 0;
194
195 final HypergeometricDistribution dist = HypergeometricDistribution.of(n, m, s);
196
197 for (int i = 1; i <= n; i++) {
198 final double p = dist.probability(i);
199 Assertions.assertEquals(0, p, () -> "p=" + p);
200 }
201 }
202
203 @Test
204 void testMath1356() {
205 final int n = 11;
206 final int m = 11;
207
208 for (int s = 0; s <= n; s++) {
209 final HypergeometricDistribution dist = HypergeometricDistribution.of(n, m, s);
210 final double p = dist.probability(s);
211 Assertions.assertEquals(1, p, () -> "p=" + p);
212 }
213 }
214
215 @Test
216 void testMath1021() {
217 final int N = 43130568;
218 final int m = 42976365;
219 final int n = 50;
220 final DiscreteDistribution.Sampler dist =
221 HypergeometricDistribution.of(N, m, n).createSampler(RandomSource.XO_SHI_RO_256_PP.create());
222
223 for (int i = 0; i < 100; i++) {
224 final int sample = dist.sample();
225 Assertions.assertTrue(0 <= sample, () -> "sample=" + sample);
226 Assertions.assertTrue(sample <= n, () -> "sample=" + sample);
227 }
228 }
229
230 @Test
231 void testAdditionalCumulativeProbabilityHighPrecision() {
232
233 testCumulativeProbabilityHighPrecision(
234 HypergeometricDistribution.of(500, 70, 300),
235 new int[] {10, 8},
236 new double[] {2.4055720603264525e-17, 1.2848174992266236e-19},
237 DoubleTolerances.relative(5e-14));
238 }
239
240 @Test
241 void testAdditionalSurvivalProbabilityHighPrecision() {
242
243 testSurvivalProbabilityHighPrecision(
244 HypergeometricDistribution.of(500, 70, 300),
245 new int[] {68, 69},
246 new double[] {4.570379934029859e-16, 7.4187180434325268e-18},
247 DoubleTolerances.relative(5e-14));
248 }
249
250 @ParameterizedTest
251 @CsvSource({
252 "1, 0, 0",
253 "1, 1, 0",
254 "1, 0, 1",
255 "1, 1, 1",
256 "2, 1, 1",
257 "2, 1, 2",
258 "2, 2, 1",
259 "2, 2, 2",
260 "3, 1, 1",
261 "3, 1, 2",
262 "3, 1, 3",
263 "3, 2, 1",
264 "3, 2, 2",
265 "3, 2, 3",
266 "3, 3, 1",
267 "3, 3, 2",
268 "3, 3, 3",
269
270 "15, 9, 7",
271 "23, 13, 11",
272 "200, 130, 70",
273 })
274 void testAdditionalInverseMapping(int populationSize, int numberOfSuccesses, int sampleSize) {
275 final HypergeometricDistribution dist = HypergeometricDistribution.of(populationSize, numberOfSuccesses, sampleSize);
276 final int[] points = IntStream.rangeClosed(dist.getSupportLowerBound(), dist.getSupportUpperBound()).toArray();
277 testCumulativeProbabilityInverseMapping(dist, points);
278 testSurvivalProbabilityInverseMapping(dist, points);
279 }
280 }