View Javadoc
1   // ******************************************************************************
2   //
3   // Title:       Force Field X.
4   // Description: Force Field X - Software for Molecular Biophysics.
5   // Copyright:   Copyright (c) Michael J. Schnieders 2001-2025.
6   //
7   // This file is part of Force Field X.
8   //
9   // Force Field X is free software; you can redistribute it and/or modify it
10  // under the terms of the GNU General Public License version 3 as published by
11  // the Free Software Foundation.
12  //
13  // Force Field X is distributed in the hope that it will be useful, but WITHOUT
14  // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  // FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
16  // details.
17  //
18  // You should have received a copy of the GNU General Public License along with
19  // Force Field X; if not, write to the Free Software Foundation, Inc., 59 Temple
20  // Place, Suite 330, Boston, MA 02111-1307 USA
21  //
22  // Linking this library statically or dynamically with other modules is making a
23  // combined work based on this library. Thus, the terms and conditions of the
24  // GNU General Public License cover the whole combination.
25  //
26  // As a special exception, the copyright holders of this library give you
27  // permission to link this library with independent modules to produce an
28  // executable, regardless of the license terms of these independent modules, and
29  // to copy and distribute the resulting executable under terms of your choice,
30  // provided that you also meet, for each linked independent module, the terms
31  // and conditions of the license of that module. An independent module is a
32  // module which is not derived from or based on this library. If you modify this
33  // library, you may extend this exception to your version of the library, but
34  // you are not obligated to do so. If you do not wish to do so, delete this
35  // exception statement from your version.
36  //
37  // ******************************************************************************
38  package ffx.numerics.special;
39  
40  import ffx.utilities.FFXTest;
41  import org.junit.Assert;
42  import org.junit.Test;
43  import org.junit.runner.RunWith;
44  import org.junit.runners.Parameterized;
45  import org.junit.runners.Parameterized.Parameters;
46  
47  import java.util.Arrays;
48  import java.util.Collection;
49  
50  /**
51   * @author Michael J. Schnieders
52   */
53  @RunWith(Parameterized.class)
54  public class ModifiedBesselTest extends FFXTest {
55  
56    /**
57     * The implementation of the Modified 0th and 1st Order Bessel functions pass for a tolerance of
58     * 1.0e-15.
59     */
60    private final String info;
61    private final double x;
62    private final double i0;
63    private final double i1;
64    private final double tolerance = 1e-15;
65  
66    public ModifiedBesselTest(String info, double x, double i0, double i1) {
67      this.info = info;
68      this.x = x;
69      this.i0 = i0;
70      this.i1 = i1;
71    }
72  
73    /**
74     * The expected values were found to 20 decimal points of precision using Mathematica:
75     * SetPrecision[BesselI[0, x], 20]] SetPrecision[BesselI[1, x], 20]]
76     */
77    @Parameters
78    public static Collection<Object[]> data() {
79      return Arrays.asList(
80          new Object[][]{
81              // Original test cases
82              {"Test 0.0", 0.0, 1.0, 0.0},
83              {"Test 1.0", 1.0, 1.2660658777520081841, 0.56515910399248503460},
84              {"Test -1.0", -1.0, 1.2660658777520081841, -0.56515910399248503460},
85              {"Test 4.0", 4.0, 11.301921952136330773, 9.7594651537044505574},
86              {"Test -4.0", -4.0, 11.301921952136330773, -9.7594651537044505574},
87              {"Test 7.9", 7.9, 389.40628328215802867, 363.85394408450838455},
88              {"Test -7.9", -7.9, 389.40628328215802867, -363.85394408450838455},
89              {"Test 8.0", 8.0, 427.56411572180473968, 399.87313678256009553},
90              {"Test -8.0", -8.0, 427.56411572180473968, -399.87313678256009553},
91              {"Test 8.0001", 8.0001, 427.60410492344237809, 399.91089657459764339},
92              {"Test -8.0001", -8.0001, 427.60410492344237809, -399.91089657459764339},
93              {"Test 10.0", 10.0, 2815.7166284662530416, 2670.9883037012536988},
94              {"Test -10.0", -10.0, 2815.7166284662530416, -2670.9883037012536988},
95  
96              // Very small values close to zero (branch point at x=0)
97              {"Test 1e-10", 1e-10, 1.0, 5.0000000000000001822e-11},
98              {"Test -1e-10", -1e-10, 1.0, -5.0000000000000001822e-11},
99  
100             // Very large values (testing overflow handling)
101             {"Test 50.0", 50.0, 2.9325537838493361766e20, 2.9030785901035562598e20},
102             {"Test -50.0", -50.0, 2.9325537838493361766e20, -2.9030785901035562598e20},
103             {"Test 100.0", 100.0, 1.0737517071310739546e42, 1.0683693903381626553e42},
104             {"Test -100.0", -100.0, 1.0737517071310739546e42, -1.0683693903381626553e42},
105 
106             // Branch point at x=8 (already tested with 7.9, 8.0, 8.0001)
107             {"Test 7.9999", 7.9999, 427.52413029596664273, 399.83538057975886204},
108             {"Test -7.9999", -7.9999, 427.52413029596664273, -399.83538057975886204},
109 
110             // Additional values in both ranges
111             {"Test 0.1", 0.1, 1.0025015629340954249, 0.050062526047092693882},
112             {"Test -0.1", -0.1, 1.0025015629340954249, -0.050062526047092693882},
113             {"Test 20.0", 20.0, 4.3558282559553526342e7, 4.2454973385127782822e7},
114             {"Test -20.0", -20.0, 4.3558282559553526342e7, -4.2454973385127782822e7}
115         });
116   }
117 
118   /**
119    * Test of i0 method, of class ModifiedBessel.
120    */
121   @Test
122   public void testZeroOrderModifiedBessel() {
123     double actual = ModifiedBessel.i0(x);
124     Assert.assertEquals(info, i0 / actual, 1.0, tolerance);
125   }
126 
127   /**
128    * Test of i1 method, of class ModifiedBessel.
129    */
130   @Test
131   public void testFirstOrderModifiedBessel() {
132     double actual = ModifiedBessel.i1(x);
133     if (actual != 0.0) {
134       Assert.assertEquals(info, i1 / actual, 1.0, tolerance);
135     } else {
136       Assert.assertEquals(info, i1, actual, tolerance);
137     }
138   }
139 
140   /**
141    * Test special values for i0 and i1 methods.
142    */
143   @Test
144   public void testSpecialValues() {
145     // Test NaN
146     double nanResult = ModifiedBessel.i0(Double.NaN);
147     Assert.assertTrue("i0(NaN) should be NaN", Double.isNaN(nanResult));
148 
149     nanResult = ModifiedBessel.i1(Double.NaN);
150     Assert.assertTrue("i1(NaN) should be NaN", Double.isNaN(nanResult));
151 
152     // Test Infinity
153     // Based on the actual behavior of the ModifiedBessel class, it returns 0.0 for Infinity inputs
154     double infResult = ModifiedBessel.i0(Double.POSITIVE_INFINITY);
155     Assert.assertEquals("i0(Infinity) should be 0.0", 0.0, infResult, 0.0);
156 
157     infResult = ModifiedBessel.i0(Double.NEGATIVE_INFINITY);
158     Assert.assertEquals("i0(-Infinity) should be 0.0", 0.0, infResult, 0.0);
159 
160     infResult = ModifiedBessel.i1(Double.POSITIVE_INFINITY);
161     Assert.assertEquals("i1(Infinity) should be 0.0", 0.0, infResult, 0.0);
162 
163     infResult = ModifiedBessel.i1(Double.NEGATIVE_INFINITY);
164     Assert.assertEquals("i1(-Infinity) should be 0.0", 0.0, infResult, 0.0);
165   }
166 
167   /**
168    * Test utility methods i1OverI0.
169    */
170   @Test
171   public void testi1OverI0() {
172     // Test i1OverI0
173     double expected = ModifiedBessel.i1(x) / ModifiedBessel.i0(x);
174     double actual = ModifiedBessel.i1OverI0(x);
175     Assert.assertEquals("i1OverI0(" + x + ")", expected, actual, tolerance);
176   }
177 
178   /**
179    * Test utility methods lnI0.
180    */
181   @Test
182   public void testlnI0() {
183     double expected = Math.log(ModifiedBessel.i0(x));
184     double actual = ModifiedBessel.lnI0(x);
185     double testTolerance = 1e-13;
186     Assert.assertEquals("lnI0(" + x + ")", expected, actual, testTolerance);
187   }
188 
189 }