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    *      http://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.MethodSource;
25  
26  /**
27   * Test cases for {@link FDistribution}.
28   * Extends {@link BaseContinuousDistributionTest}. See javadoc of that class for details.
29   */
30  class FDistributionTest extends BaseContinuousDistributionTest {
31      @Override
32      ContinuousDistribution makeDistribution(Object... parameters) {
33          final double df1 = (Double) parameters[0];
34          final double df2 = (Double) parameters[1];
35          return FDistribution.of(df1, df2);
36      }
37  
38      @Override
39      Object[][] makeInvalidParameters() {
40          return new Object[][] {
41              {0.0, 1.0},
42              {-0.1, 1.0},
43              {1.0, 0.0},
44              {1.0, -0.1},
45          };
46      }
47  
48      @Override
49      String[] getParameterNames() {
50          return new String[] {"NumeratorDegreesOfFreedom", "DenominatorDegreesOfFreedom"};
51      }
52  
53      @Override
54      protected double getRelativeTolerance() {
55          return 8e-15;
56      }
57  
58      //-------------------- Additional test cases -------------------------------
59  
60      @ParameterizedTest
61      @MethodSource
62      void testAdditionalMoments(double numeratorDegreesOfFreedom,
63                                 double denominatorDegreesOfFreedom,
64                                 double mean,
65                                 double variance) {
66          final FDistribution dist = FDistribution.of(numeratorDegreesOfFreedom, denominatorDegreesOfFreedom);
67          testMoments(dist, mean, variance, DoubleTolerances.equals());
68      }
69  
70      static Stream<Arguments> testAdditionalMoments() {
71          return Stream.of(
72              Arguments.of(1, 2, Double.NaN, Double.NaN),
73              Arguments.of(1, 3, 3.0 / (3 - 2), Double.NaN),
74              Arguments.of(1, 5, 5.0 / (5 - 2), (2 * 5 * 5 * 4) / 9.0)
75          );
76      }
77  
78      @Test
79      void testLargeDegreesOfFreedom() {
80          final double x0 = 0.999;
81          final FDistribution fd = FDistribution.of(100000, 100000);
82          final double p = fd.cumulativeProbability(x0);
83          final double x = fd.inverseCumulativeProbability(p);
84          Assertions.assertEquals(x0, x, 1.0e-5);
85      }
86  
87      @Test
88      void testSmallDegreesOfFreedom() {
89          final double x0 = 0.975;
90          FDistribution fd = FDistribution.of(1, 1);
91          double p = fd.cumulativeProbability(x0);
92          double x = fd.inverseCumulativeProbability(p);
93          Assertions.assertEquals(x0, x, 1.0e-5);
94  
95          fd = FDistribution.of(1, 2);
96          p = fd.cumulativeProbability(x0);
97          x = fd.inverseCumulativeProbability(p);
98          Assertions.assertEquals(x0, x, 1.0e-5);
99      }
100 
101     @Test
102     void testMath785() {
103         // this test was failing due to inaccurate results from ContinuedFraction.
104         final double prob = 0.01;
105         final FDistribution f = FDistribution.of(200000, 200000);
106         final double result = f.inverseCumulativeProbability(prob);
107         Assertions.assertTrue(result < 1.0, "Failing to calculate inverse cumulative probability");
108     }
109 
110     @ParameterizedTest
111     @MethodSource
112     void testAdditionalLogDensity(double numeratorDegreesOfFreedom,
113                                   double denominatorDegreesOfFreedom,
114                                   double[] points,
115                                   double[] values) {
116         testLogDensity(FDistribution.of(numeratorDegreesOfFreedom, denominatorDegreesOfFreedom),
117             points, values, createRelTolerance(1e-15));
118     }
119 
120     static Stream<Arguments> testAdditionalLogDensity() {
121         // Computed using Boost multiprecision to 100 digits (output 25 digits).
122 
123         // Edge cases when the standard density is sub-normal or zero.
124         final double[] x = new double[] {1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9};
125         return Stream.of(
126             Arguments.of(100, 100, x,
127                 new double[] {
128                     -56.96014024624318913110565,
129                     -165.8559950938238412964495,
130                     -282.3927517845117162132265,
131                     -399.7346409939330236149964,
132                     -517.157481231596055999464,
133                     -634.5884209792423525846316,
134                     -752.0201707219881824362492,
135                     -869.4520014646850073211334,
136                     -986.883840307381342156051}),
137             Arguments.of(952, 912, x,
138                 new double[] {
139                     -509.5128641158461391223255,
140                     -1485.417858108384337659572,
141                     -2529.705750311339816652123,
142                     -3581.184004620825529681231,
143                     -4633.385040722443349533971,
144                     -5685.658392700035382988623,
145                     -6737.938976642435125691553,
146                     -7790.220283785087985050541,
147                     -8842.501663247803879775318}),
148             // This causes intermediate overflow of the density function
149             Arguments.of(1e-100, 1,
150                 new double[] {1e-200, 1e-250, 1e-300},
151                 new double[] {
152                     229.5653621188446231302736,
153                     344.6946167685469072592738,
154                     459.8238714182491914891139})
155         );
156     }
157 
158     @ParameterizedTest
159     @MethodSource
160     void testAdditionalDensity(double numeratorDegreesOfFreedom,
161                                double denominatorDegreesOfFreedom,
162                                double[] points,
163                                double[] values,
164                                double relativeError) {
165         testDensity(FDistribution.of(numeratorDegreesOfFreedom, denominatorDegreesOfFreedom),
166             points, values, createRelTolerance(relativeError));
167     }
168 
169     static Stream<Arguments> testAdditionalDensity() {
170         // Computed using Boost multiprecision to 100 digits (output 25 digits).
171 
172         // Edge cases when the standard density is sub-normal.
173         return Stream.of(
174             Arguments.of(100, 100,
175                 new double[] {1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 3e6, 4e6, 4.5e6, 5e6, 1e7},
176                 new double[] {
177                     1.830313161302986740491046e-25,
178                     9.325165326363852979476269e-73,
179                     2.282370632180103030176872e-123,
180                     2.49718772086196154389661e-174,
181                     2.51976260334572601372639e-225,
182                     2.522031398014840106819471e-276,
183                     1.171103964711921105069224e-300,
184                     4.97420298008526384736197e-307,
185                     1.224464123468993962344698e-309,
186                     5.679564178845752345371413e-312,
187                     0
188                 }, 3e-13),
189             Arguments.of(952, 912,
190                 new double[] {10, 11, 12, 13, 14, 15, 16, 17, 18},
191                 new double[] {5.264712450643104177155291e-222,
192                     1.083049754753448067375765e-237,
193                     2.996024821196787172008532e-252,
194                     7.919262482129153149257417e-266,
195                     1.511696585130734458293958e-278,
196                     1.652611434344889324846565e-290,
197                     8.522337060963566999523664e-302,
198                     1.760000675560273604454495e-312,
199                     1.266172656954210816606837e-322
200                 }, 2e-13),
201             // This causes intermediate overflow of the density function
202             Arguments.of(1e-100, 1,
203                 new double[] {1e-200, 1e-250, 1e-300},
204                 new double[] {
205                     5.000000000000000189458187e+99,
206                     4.999999999999999829961813e+149,
207                     4.99999999999999997466404e+199
208                 }, 5e-14)
209         );
210     }
211 }