View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.statistics.distribution;
18  
19  import java.util.stream.Stream;
20  import org.junit.jupiter.api.Assertions;
21  import org.junit.jupiter.api.Test;
22  import org.junit.jupiter.params.ParameterizedTest;
23  import org.junit.jupiter.params.provider.Arguments;
24  import org.junit.jupiter.params.provider.CsvSource;
25  import org.junit.jupiter.params.provider.MethodSource;
26  import org.junit.jupiter.params.provider.ValueSource;
27  
28  /**
29   * Test cases for {@link TDistribution}.
30   * Extends {@link BaseContinuousDistributionTest}. See javadoc of that class for details.
31   */
32  class TDistributionTest extends BaseContinuousDistributionTest {
33      @Override
34      ContinuousDistribution makeDistribution(Object... parameters) {
35          final double df = (Double) parameters[0];
36          return TDistribution.of(df);
37      }
38  
39      @Override
40      Object[][] makeInvalidParameters() {
41          return new Object[][] {
42              {0.0},
43              {-0.1}
44          };
45      }
46  
47      @Override
48      String[] getParameterNames() {
49          return new String[] {"DegreesOfFreedom"};
50      }
51  
52      @Override
53      protected double getRelativeTolerance() {
54          return 1e-15;
55      }
56  
57      //-------------------- Additional test cases -------------------------------
58  
59      @ParameterizedTest
60      @MethodSource
61      void testAdditionalMoments(double df, double mean, double variance) {
62          final TDistribution dist = TDistribution.of(df);
63          testMoments(dist, mean, variance, DoubleTolerances.equals());
64      }
65  
66      static Stream<Arguments> testAdditionalMoments() {
67          return Stream.of(
68              Arguments.of(1.5, 0, Double.POSITIVE_INFINITY),
69              Arguments.of(2.1, 0, 2.1 / (2.1 - 2.0)),
70              Arguments.of(12.1, 0, 12.1 / (12.1 - 2.0))
71          );
72      }
73  
74      /**
75       * @see <a href="https://issues.apache.orgg/bugzilla/show_bug.cgi?id=27243">
76       *      Bug report that prompted this unit test.</a>
77       */
78      @Test
79      void testCumulativeProbabilityAgainstStackOverflow() {
80          final TDistribution td = TDistribution.of(5.);
81          Assertions.assertDoesNotThrow(() -> {
82              td.cumulativeProbability(.1);
83              td.cumulativeProbability(.01);
84          });
85      }
86  
87      /*
88       * Adding this test to benchmark against tables published by NIST
89       * http://itl.nist.gov/div898/handbook/eda/section3/eda3672.htm
90       * Have chosen tabulated results for degrees of freedom 2,10,30,100
91       * Have chosen problevels from 0.10 to 0.001
92       */
93      @ParameterizedTest
94      @MethodSource
95      void testNistData(double t, double[] x, double[] expected) {
96          // Data points are not very exact so use a low tolerance.
97          final DoubleTolerance tolerance = createAbsTolerance(1e-4);
98          testSurvivalProbability(TDistribution.of(t), x, expected, tolerance);
99      }
100 
101     static Stream<Arguments> testNistData() {
102         final double[] prob = new double[]{0.10, 0.05, 0.025, 0.01, 0.005, 0.001};
103         return Stream.of(
104             Arguments.of(2, new double[] {1.886, 2.920, 4.303, 6.965, 9.925, 22.327}, prob),
105             Arguments.of(10, new double[]{1.372, 1.812, 2.228, 2.764, 3.169, 4.143}, prob),
106             Arguments.of(30, new double[]{1.310, 1.697, 2.042, 2.457, 2.750, 3.385}, prob),
107             Arguments.of(100, new double[]{1.290, 1.660, 1.984, 2.364, 2.626, 3.174}, prob)
108         );
109     }
110 
111 
112     // See https://issues.apache.org/jira/browse/STATISTICS-25
113     @ParameterizedTest
114     @CsvSource({
115         // Data from r stats TDist
116         "1.00E+00, 0.025, 0.31811106676706130125, 0.50795608991202578775",
117         "1.00E+01, 0.025, 0.38897465512398698984, 0.50972659510159001872",
118         "1.00E+02, 0.025, 0.39782060538246560855, 0.50994760809308248284",
119         "1.00E+03, 0.025, 0.39871781392704330749, 0.50997002433945715083",
120         "1.00E+04, 0.025, 0.39880764764142323520, 0.50997226878155033081",
121         "1.00E+05, 0.025, 0.39881663212763956983, 0.50997249325358851024",
122         "1.00E+06, 0.025, 0.39881753058739516371, 0.50997251570107027252",
123         "2.00E+06, 0.025, 0.39881758050188537146, 0.50997251694815404210",
124         "2.98E+06, 0.025, 0.39881759691671903045, 0.50997251735826898411",
125         "2.99E+06, 0.025, 0.39881759702875807516, 0.50997251736106818942",
126         "3.00E+06, 0.025, 0.39881759714005016182, 0.50997251736384874299",
127         "4.00E+06, 0.025, 0.39881760545913280680, 0.50997251757169603792",
128         "1.00E+07, 0.025, 0.39881762043348206737, 0.50997251794582121320",
129         "1.00E+08, 0.025, 0.39881762941809190126, 0.50997251817029631837",
130         "1.00E+09, 0.025, 0.39881763031655281804, 0.50997251819274391771",
131         "1.00E+10, 0.025, 0.39881763040639894857, 0.50997251819498856662",
132         "1.00E+11, 0.025, 0.39881763041538353942, 0.50997251819521305372",
133         "1.00E+12, 0.025, 0.39881763041628198740, 0.50997251819523548022",
134         "1.00E+13, 0.025, 0.39881763041637185996, 0.50997251819523781169",
135         "1.00E+14, 0.025, 0.39881763041638085276, 0.50997251819523803373",
136         "1.00E+15, 0.025, 0.39881763041638174094, 0.50997251819523803373",
137         "1.00E+16, 0.025, 0.39881763041638179645, 0.50997251819523803373",
138         "1.00E+17, 0.025, 0.39881763041638179645, 0.50997251819523803373",
139         "1.00E+18, 0.025, 0.39881763041638179645, 0.50997251819523803373",
140     })
141     void testStatistics25(double df, double x, double pdf, double cdf) {
142         final TDistribution dist = TDistribution.of(df);
143 
144         final double density = dist.density(x);
145         Assertions.assertEquals(pdf, density, 4 * Math.ulp(pdf),
146             () -> "pdf error: " + (Double.doubleToLongBits(pdf) - Double.doubleToRawLongBits(density)));
147 
148         final double p = dist.cumulativeProbability(x);
149         Assertions.assertEquals(cdf, p, 6 * Math.ulp(cdf),
150             () -> "cdf error: " + (Double.doubleToLongBits(cdf) - Double.doubleToRawLongBits(p)));
151     }
152 
153     /**
154      * Test the inverse probability edge case for p=0.5.
155      * The inverse can use symmetry {@code isf(p) = -icdf(p)}. When p=0.5 the inverse
156      * will generate -0.0 for the negation. This test ensures both return the more logical 0.0.
157      */
158     @ParameterizedTest
159     @ValueSource(doubles = {1, 42, 1e25})
160     void testInverseSymmetryEdgeCase(double df) {
161         final TDistribution dist = TDistribution.of(df);
162         final double p = 0.5;
163         final double t = 0.0;
164         Assertions.assertEquals(t, dist.inverseCumulativeProbability(p));
165         Assertions.assertEquals(t, dist.inverseSurvivalProbability(p));
166     }
167 }