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 org.junit.jupiter.api.Assertions;
20  import org.junit.jupiter.api.Test;
21  
22  /**
23   * Test cases for {@link LogisticDistribution}.
24   * Extends {@link BaseContinuousDistributionTest}. See javadoc of that class for details.
25   */
26  class LogisticDistributionTest extends BaseContinuousDistributionTest {
27      @Override
28      ContinuousDistribution makeDistribution(Object... parameters) {
29          final double location = (Double) parameters[0];
30          final double scale = (Double) parameters[1];
31          return LogisticDistribution.of(location, scale);
32      }
33  
34      @Override
35      Object[][] makeInvalidParameters() {
36          return new Object[][] {
37              {0.0, 0.0},
38              {0.0, -0.1}
39          };
40      }
41  
42      @Override
43      String[] getParameterNames() {
44          return new String[] {"Location", "Scale"};
45      }
46  
47      @Override
48      protected double getRelativeTolerance() {
49          return 5e-15;
50      }
51  
52      //-------------------- Additional test cases -------------------------------
53  
54      @Test
55      void testExtremeDensity() {
56          final LogisticDistribution dist = LogisticDistribution.of(0, 1.0);
57          // Direct density (with scale = 1):
58          // exp(-x) / (1 + exp(-x))^2
59          // As x -> large negative then exp(-x) will overflow and a simple
60          // computation will be incorrect
61          final double x0 = -710;
62          Assertions.assertEquals(Double.POSITIVE_INFINITY, Math.exp(-x0));
63          Assertions.assertEquals(Double.NaN, Math.exp(-x0) / Math.pow(1 + Math.exp(-x0), 2.0));
64  
65          // Computed using scipy.stats logistic.
66          // These values of exp(x) will create overflow.
67          final double[] x = {710, 720, 730, 740, 750};
68          final double[] values = {4.47628622567513e-309,
69              2.03223080241836e-313, 9.22631526816382e-318,
70              4.19955798965060e-322, 0.00000000000000e+000};
71          testDensity(dist, x, values, DoubleTolerances.equals());
72  
73          // Test symmetry.
74          for (final double xi : x) {
75              Assertions.assertEquals(dist.density(xi), dist.density(-xi), () -> Double.toString(xi));
76          }
77      }
78  
79      /**
80       * Test a value for log density when the density computation is zero.
81       */
82      @Test
83      void testExtremeLogDensity() {
84          final double scale = 2.5;
85          final LogisticDistribution dist = LogisticDistribution.of(0, scale);
86          // Direct density (with scale = s):
87          // exp(-x / s) / (1 + exp(-x / s))^2
88          final double x = 1e160;
89          Assertions.assertEquals(0.0, dist.density(x));
90          // Log computation
91          final double expected = -x / scale - 2 * Math.log1p(Math.exp(-x / scale));
92          Assertions.assertNotEquals(Double.NEGATIVE_INFINITY, expected, "Density is zero but log density should not be -infinity");
93          Assertions.assertEquals(expected, dist.logDensity(x), Math.abs(expected) * 1e-15);
94      }
95  }