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 org.apache.commons.rng.UniformRandomProvider;
21 import org.apache.commons.rng.simple.RandomSource;
22 import org.junit.jupiter.api.Assertions;
23 import org.junit.jupiter.api.Test;
24 import org.junit.jupiter.params.ParameterizedTest;
25 import org.junit.jupiter.params.provider.CsvSource;
26
27
28
29
30
31 class ParetoDistributionTest extends BaseContinuousDistributionTest {
32
33
34
35
36
37 private static final double U = 0x1.0p-53;
38
39 @Override
40 ContinuousDistribution makeDistribution(Object... parameters) {
41 final double scale = (Double) parameters[0];
42 final double shape = (Double) parameters[1];
43 return ParetoDistribution.of(scale, shape);
44 }
45
46 @Override
47 Object[][] makeInvalidParameters() {
48 return new Object[][] {
49 {0.0, 1.0},
50 {-0.1, 1.0},
51 {1.0, 0.0},
52 {1.0, -0.1},
53 {Double.POSITIVE_INFINITY, 1.0},
54 };
55 }
56
57 @Override
58 String[] getParameterNames() {
59 return new String[] {"Scale", "Shape"};
60 }
61
62 @Override
63 protected double getRelativeTolerance() {
64 return 5e-15;
65 }
66
67
68
69 @ParameterizedTest
70 @CsvSource({
71 "1, 1, Infinity, Infinity",
72 "2.2, 2.4, 3.771428571428, 14.816326530",
73 })
74 void testAdditionalMoments(double scale, double shape, double mean, double variance) {
75 final ParetoDistribution dist = ParetoDistribution.of(scale, shape);
76 testMoments(dist, mean, variance, createRelTolerance(1e-9));
77 }
78
79 @Test
80 void testAdditionalCumulativeProbabilityHighPrecision() {
81 final double scale = 2.1;
82
83 final double[] x = {Math.nextUp(scale), Math.nextUp(Math.nextUp(scale))};
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100 final ParetoDistribution dist = ParetoDistribution.of(scale, 0.75);
101
102
103
104
105
106 final double[] values = {1.5860328923216517E-16, 3.172065784643303E-16};
107 testCumulativeProbabilityHighPrecision(dist, x, values, createRelTolerance(0.05));
108 }
109
110 @Test
111 void testAdditionalCumulativeProbabilityHighPrecision2() {
112 final double scale = 3;
113
114 final double[] x = {Math.nextUp(scale), Math.nextUp(Math.nextUp(scale))};
115
116
117
118
119
120
121 final ParetoDistribution dist = ParetoDistribution.of(3, 0.25);
122
123 final double[] values = {3.700743415417188E-17, 7.401486830834375E-17};
124 testCumulativeProbabilityHighPrecision(dist, x, values, createAbsTolerance(1e-17));
125
126 final ParetoDistribution dist2 = ParetoDistribution.of(3, 1.5);
127
128 final double[] values2 = {2.2204460492503126E-16, 4.4408920985006247E-16};
129 testCumulativeProbabilityHighPrecision(dist2, x, values2, createAbsTolerance(6e-17));
130 }
131
132 @Test
133 void testAdditionalSurvivalProbabilityHighPrecision() {
134 final ParetoDistribution dist = ParetoDistribution.of(2.1, 1.4);
135 testSurvivalProbabilityHighPrecision(
136 dist,
137 new double[] {42e11, 64e11},
138 new double[] {6.005622169907148e-18, 3.330082930386111e-18},
139 DoubleTolerances.relative(5e-14));
140 }
141
142
143
144
145 @Test
146 void testExtremeValues() {
147 final ParetoDistribution dist = ParetoDistribution.of(1, 1);
148 for (int i = 0; i < 10000; i++) {
149 final double upperTail = dist.cumulativeProbability(i);
150 if (i <= 1000) {
151 Assertions.assertTrue(upperTail < 1.0d);
152 } else {
153 Assertions.assertTrue(upperTail > 0.999);
154 }
155 }
156
157 Assertions.assertEquals(1, dist.cumulativeProbability(Double.MAX_VALUE));
158 Assertions.assertEquals(0, dist.cumulativeProbability(-Double.MAX_VALUE));
159 Assertions.assertEquals(1, dist.cumulativeProbability(Double.POSITIVE_INFINITY));
160 Assertions.assertEquals(0, dist.cumulativeProbability(Double.NEGATIVE_INFINITY));
161 }
162
163
164
165
166
167
168 @Test
169 void testExtremeParameters() {
170 double scale;
171 double shape;
172
173
174 scale = 10;
175 shape = 306;
176 Assertions.assertEquals(Double.POSITIVE_INFINITY, shape * Math.pow(scale, shape));
177 Assertions.assertTrue(Double.isFinite(Math.log(shape) + Math.log(scale) * shape));
178
179
180
181
182 scale = 10;
183 shape = Double.POSITIVE_INFINITY;
184 Assertions.assertEquals(Double.POSITIVE_INFINITY, shape * Math.pow(scale, shape));
185 Assertions.assertEquals(Double.POSITIVE_INFINITY, Math.log(shape) + Math.log(scale) * shape);
186
187
188 shape = 1e300;
189 Assertions.assertEquals(Double.POSITIVE_INFINITY, shape * Math.pow(scale, shape));
190 Assertions.assertTrue(Double.isFinite(Math.log(shape) + Math.log(scale) * shape));
191
192
193
194
195 scale = 1;
196 shape = Double.POSITIVE_INFINITY;
197
198 Assertions.assertEquals(Double.NaN, shape * Math.pow(scale, shape));
199
200 Assertions.assertEquals(Double.NaN, Math.log(shape) + Math.log(scale) * shape);
201
202
203 shape = 1e300;
204 Assertions.assertEquals(shape, shape * Math.pow(scale, shape));
205 Assertions.assertTrue(Double.isFinite(Math.log(shape) + Math.log(scale) * shape));
206
207
208
209
210 scale = 0.1;
211 shape = 324;
212 Assertions.assertEquals(0.0, shape * Math.pow(scale, shape));
213 Assertions.assertTrue(Double.isFinite(Math.log(shape) + Math.log(scale) * shape));
214
215
216
217
218 scale = 0.1;
219 shape = Double.MAX_VALUE;
220 Assertions.assertEquals(0.0, shape * Math.pow(scale, shape));
221 Assertions.assertEquals(Double.NEGATIVE_INFINITY, Math.log(shape) + Math.log(scale) * shape);
222
223
224
225
226
227
228 scale = 0.1;
229 shape = Double.POSITIVE_INFINITY;
230 Assertions.assertEquals(Double.NaN, shape * Math.pow(scale, shape));
231 Assertions.assertEquals(Double.NaN, Math.log(shape) + Math.log(scale) * shape);
232
233
234
235
236
237
238
239 shape = Double.MIN_VALUE;
240 for (final double scale2 : new double[] {Double.MIN_VALUE, 0.1, 1, 10, 100}) {
241 Assertions.assertEquals(shape, shape * Math.pow(scale2, shape));
242 Assertions.assertTrue(Double.isFinite(Math.log(shape) + Math.log(scale2) * shape));
243 }
244 }
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289 @ParameterizedTest
290 @CsvSource({
291
292 "10, Infinity",
293 "1, Infinity",
294 "0.1, Infinity",
295
296
297
298
299
300
301 "10, 6.6179136542552806e17",
302 "1, 6.6179136542552806e17",
303 "0.1, 6.6179136542552806e17",
304 })
305 void testSamplingWithLargeShape(double scale, double shape) {
306 final ParetoDistribution dist = ParetoDistribution.of(scale, shape);
307
308
309 final double x0 = dist.inverseCumulativeProbability(0);
310 final double x1 = dist.inverseCumulativeProbability(1 - U);
311 Assertions.assertEquals(scale, x0);
312 Assertions.assertEquals(x0, x1, "Test parameters did not create an extreme distribution");
313
314
315 assertSampler(dist, scale);
316 }
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334 @ParameterizedTest
335 @CsvSource({
336
337
338 "10, 4.9e-324",
339 "1, 4.9e-324",
340 "0.1, 4.9e-324",
341
342
343
344
345
346 "10, 5.56268464626801E-309",
347 "1, 5.56268464626801E-309",
348 "0.1, 5.56268464626801E-309",
349
350
351
352
353
354
355
356 "10, 7.456765604783329e-20",
357 "1, 7.456765604783329e-20",
358
359 "4.9e-324, 7.456765604783329e-20",
360 })
361 void testSamplingWithTinyShape(double scale, double shape) {
362 final ParetoDistribution dist = ParetoDistribution.of(scale, shape);
363
364
365 final double x0 = dist.inverseCumulativeProbability(U);
366 final double x1 = dist.inverseCumulativeProbability(1);
367 Assertions.assertEquals(Double.POSITIVE_INFINITY, x1);
368 Assertions.assertEquals(x1, x0, "Test parameters did not create an extreme distribution");
369
370
371 assertSampler(dist, Double.POSITIVE_INFINITY);
372 }
373
374
375
376
377
378
379
380 private static void assertSampler(ParetoDistribution dist, double expected) {
381
382
383 final long[] values = {0, -1, 1, 1L << 11, -2, -2L << 11};
384 final UniformRandomProvider rng = createRNG(values);
385 ContinuousDistribution.Sampler s = dist.createSampler(rng);
386 for (final long l : values) {
387 Assertions.assertEquals(expected, s.sample(), () -> "long bits = " + l);
388 }
389
390 final long seed = RandomSource.createLong();
391 s = dist.createSampler(RandomSource.SPLIT_MIX_64.create(seed));
392 for (int i = 0; i < 100; i++) {
393 Assertions.assertEquals(expected, s.sample(), () -> "seed = " + seed);
394 }
395 }
396
397
398
399
400
401
402
403 private static UniformRandomProvider createRNG(long... values) {
404 return new UniformRandomProvider() {
405 private int i;
406
407 @Override
408 public long nextLong() {
409 return values[i++];
410 }
411
412 @Override
413 public double nextDouble() {
414 throw new IllegalStateException("nextDouble cannot be trusted to be in [0, 1) and should be ignored");
415 }
416 };
417 }
418 }