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.math.BigDecimal;
21 import java.math.MathContext;
22 import java.util.stream.Stream;
23 import org.junit.jupiter.api.Assertions;
24 import org.junit.jupiter.api.Test;
25 import org.junit.jupiter.params.ParameterizedTest;
26 import org.junit.jupiter.params.provider.Arguments;
27 import org.junit.jupiter.params.provider.MethodSource;
28
29
30
31
32
33 class TrapezoidalDistributionTest extends BaseContinuousDistributionTest {
34 @Override
35 ContinuousDistribution makeDistribution(Object... parameters) {
36 final double a = (Double) parameters[0];
37 final double b = (Double) parameters[1];
38 final double c = (Double) parameters[2];
39 final double d = (Double) parameters[3];
40 return TrapezoidalDistribution.of(a, b, c, d);
41 }
42
43 @Override
44 Object[][] makeInvalidParameters() {
45 return new Object[][] {
46 {0.0, 0.0, 0.0, 0.0},
47
48 {5.0, 2.0, 3.0, 4.0},
49 {1.0, 5.0, 3.0, 4.0},
50 {1.0, 2.0, 5.0, 4.0},
51 {3.5, 2.0, 3.0, 4.0},
52 {1.0, 3.5, 3.0, 4.0},
53 {2.5, 2.0, 3.0, 4.0},
54 {1.0, 2.0, 3.0, 0.0},
55 {1.0, 2.0, 0.0, 4.0},
56 {1.0, 0.0, 3.0, 4.0},
57 {1.0, 2.0, 3.0, 1.5},
58 {1.0, 2.0, 1.5, 4.0},
59 {1.0, 2.0, 3.0, 2.5},
60 };
61 }
62
63 @Override
64 String[] getParameterNames() {
65 return new String[] {"SupportLowerBound", "B", "C", "SupportUpperBound"};
66 }
67
68 @Override
69 protected double getRelativeTolerance() {
70
71 return 20 * RELATIVE_EPS;
72 }
73
74
75
76 @ParameterizedTest
77 @MethodSource
78 void testAdditionalMoments(double a, double b, double c, double d, double mean, double variance, int ulps) {
79 final TrapezoidalDistribution dist = TrapezoidalDistribution.of(a, b, c, d);
80 testMoments(dist, mean, variance, DoubleTolerances.ulps(ulps));
81 }
82
83 static Stream<Arguments> testAdditionalMoments() {
84 return Stream.of(
85
86
87 Arguments.of(0, 0.1, 1, 1, 0.5245614035087719, 0.07562480763311791, 8),
88 Arguments.of(0, 1e-3, 1, 1, 0.5002499583124894, 0.08325006249999839, 8),
89 Arguments.of(0, 1e-6, 1, 1, 0.5000002499999582, 0.08333325000006259, 8),
90 Arguments.of(0, 1e-9, 1, 1, 0.50000000025, 0.08333333324999997, 4),
91 Arguments.of(0, 1e-12, 1, 1, 0.50000000000025, 0.08333333333324999, 4),
92 Arguments.of(0, 1e-15, 1, 1, 0.5000000000000003, 0.0833333333333332, 4),
93 Arguments.of(0, 0, 1, 1, 0.5, 0.08333333333333331, 1),
94
95 Arguments.of(0, 0, 0.9, 1, 0.47543859649122816, 0.07562480763311777, 4),
96 Arguments.of(0, 0, 0.999, 1, 0.49975004168751025, 0.08325006249999842, 4),
97 Arguments.of(0, 0, 0.999999, 1, 0.4999997500000417, 0.08333325000006248, 2),
98 Arguments.of(0, 0, 0.999999999, 1, 0.49999999975000003, 0.08333333325000003, 1),
99 Arguments.of(0, 0, 0.999999999999, 1, 0.49999999999975003, 0.08333333333324999, 1),
100 Arguments.of(0, 0, 0.999999999999999, 1, 0.4999999999999998, 0.08333333333333326, 1),
101 Arguments.of(0, 0, 1, 1, 0.5, 0.08333333333333331, 1),
102
103 moments(-3, 1, 4, 12, 10),
104 moments(1, 2, 3, 4, 4),
105 moments(11, 15, 22, 30, 4),
106 moments(0, 1, 9, 10, 4),
107 moments(-12, -10, -2, -1, 8)
108 );
109 }
110
111
112
113
114
115
116 private static Arguments moments(double a, double b, double c, double d, int ulps) {
117 final BigDecimal aa = new BigDecimal(a);
118 final BigDecimal bb = new BigDecimal(b);
119 final BigDecimal cc = new BigDecimal(c);
120 final BigDecimal dd = new BigDecimal(d);
121 final MathContext mc = MathContext.DECIMAL128;
122
123 BigDecimal divisor = dd.add(cc).subtract(aa).subtract(bb).multiply(BigDecimal.valueOf(3));
124 BigDecimal dc = dd.pow(3).subtract(cc.pow(3)).divide(dd.subtract(cc), mc);
125 BigDecimal ba = bb.pow(3).subtract(aa.pow(3)).divide(bb.subtract(aa), mc);
126 final BigDecimal mu = dc.subtract(ba).divide(divisor, mc);
127
128 divisor = divisor.multiply(BigDecimal.valueOf(2));
129 dc = dd.pow(4).subtract(cc.pow(4)).divide(dd.subtract(cc), mc);
130 ba = bb.pow(4).subtract(aa.pow(4)).divide(bb.subtract(aa), mc);
131 final BigDecimal var = dc.subtract(ba).divide(divisor, mc).subtract(mu.pow(2));
132 return Arguments.of(a, b, c, d, mu.doubleValue(), var.doubleValue(), ulps);
133 }
134
135
136
137
138
139 @Test
140 void testAdditionalSurvivalProbabilityHighPrecision() {
141 final double a = 0;
142 final double b = 0;
143 final double c = 1;
144 final double d = 1 << 14;
145 final double x1 = Math.nextDown(d);
146 final double x2 = Math.nextDown(x1);
147 final double p1 = survivalProbability(a, b, c, d, x1);
148 final double p2 = survivalProbability(a, b, c, d, x2);
149 final TrapezoidalDistribution dist = TrapezoidalDistribution.of(a, b, c, d);
150 final double[] points = {x1, x2};
151 final double[] probabilities = {p1, p2};
152
153
154 testSurvivalProbabilityHighPrecision(
155 dist,
156 points,
157 probabilities,
158 DoubleTolerances.relative(1e-15));
159
160
161 testInverseSurvivalProbability(
162 dist,
163 probabilities,
164 points,
165 DoubleTolerances.ulps(0));
166 }
167
168
169
170
171
172
173
174
175
176
177
178
179 private static double survivalProbability(double a, double b, double c, double d, double x) {
180 Assertions.assertTrue(c <= x && x <= d, "Domain error");
181 final BigDecimal aa = new BigDecimal(a);
182 final BigDecimal bb = new BigDecimal(b);
183 final BigDecimal cc = new BigDecimal(c);
184 final BigDecimal dd = new BigDecimal(d);
185 final BigDecimal divisor = dd.add(cc).subtract(aa).subtract(bb).multiply(dd.subtract(cc));
186 return dd.subtract(new BigDecimal(x)).pow(2)
187 .divide(divisor, MathContext.DECIMAL128).doubleValue();
188 }
189 }