View Javadoc
1   /*
2    * Licensed to the Hipparchus project 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 Hipparchus project 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.hipparchus.special.elliptic.jacobi;
18  
19  import java.util.function.DoubleFunction;
20  import java.util.function.Function;
21  
22  import org.hipparchus.CalculusFieldElement;
23  import org.hipparchus.Field;
24  import org.hipparchus.analysis.differentiation.UnivariateDerivative1;
25  import org.hipparchus.dfp.Dfp;
26  import org.hipparchus.dfp.DfpField;
27  import org.hipparchus.util.Binary64Field;
28  import org.hipparchus.util.FastMath;
29  import org.junit.Assert;
30  import org.junit.Test;
31  
32  public class FieldJacobiEllipticTest {
33  
34      @Test
35      public void testCircular() {
36          doTestCircular(Binary64Field.getInstance());
37      }
38  
39      @Test
40      public void testHyperbolic() {
41          doTestHyperbolic(Binary64Field.getInstance());
42      }
43  
44      @Test
45      public void testNoConvergence() {
46          doTestNoConvergence(Binary64Field.getInstance());
47      }
48  
49      @Test
50      public void testNegativeParameter() {
51          doTestNegativeParameter(Binary64Field.getInstance());
52      }
53  
54      @Test
55      public void testAbramowitzStegunExample1() {
56          doTestAbramowitzStegunExample1(Binary64Field.getInstance());
57      }
58  
59      @Test
60      public void testAbramowitzStegunExample2() {
61          doTestAbramowitzStegunExample2(Binary64Field.getInstance());
62      }
63  
64      @Test
65      public void testAbramowitzStegunExample3() {
66          doTestAbramowitzStegunExample3(Binary64Field.getInstance());
67      }
68  
69      @Test
70      public void testAbramowitzStegunExample4() {
71          doTestAbramowitzStegunExample4(Binary64Field.getInstance());
72      }
73  
74      @Test
75      public void testAbramowitzStegunExample5() {
76          doTestAbramowitzStegunExample5(Binary64Field.getInstance());
77      }
78  
79      @Test
80      public void testAbramowitzStegunExample7() {
81          doTestAbramowitzStegunExample7(Binary64Field.getInstance());
82      }
83  
84      @Test
85      public void testAbramowitzStegunExample8() {
86          doTestAbramowitzStegunExample8(Binary64Field.getInstance());
87      }
88  
89      @Test
90      public void testAbramowitzStegunExample9() {
91          doTestAbramowitzStegunExample9(Binary64Field.getInstance());
92      }
93  
94      @Test
95      public void testAllFunctions() {
96          doTestAllFunctions(Binary64Field.getInstance());
97      }
98  
99      @Test
100     public void testHighAccuracy() {
101         final DfpField field = new DfpField(60);
102         FieldJacobiElliptic<Dfp> je = JacobiEllipticBuilder.build(field.newDfp("0.75"));
103         Dfp sn        = je.valuesN(field.newDfp("1.3")).sn();
104         // this value was computed using Wolfram Alpha
105         Dfp reference = field.newDfp("0.8929235150418389265984488063926925504375953835259703680383");
106         Assert.assertTrue(sn.subtract(reference).abs().getReal() < 5.0e-58);
107     }
108 
109     @Test
110     public void testInverseCopolarN() {
111         doTestInverseCopolarN(Binary64Field.getInstance());
112     }
113 
114     @Test
115     public void testInverseCopolarS() {
116         doTestInverseCopolarS(Binary64Field.getInstance());
117     }
118 
119     @Test
120     public void testInverseCopolarC() {
121         doTestInverseCopolarC(Binary64Field.getInstance());
122     }
123 
124     @Test
125     public void testInverseCopolarD() {
126         doTestInverseCopolarD(Binary64Field.getInstance());
127     }
128 
129     @Test
130     public void testDerivatives() {
131 
132         final double m  = 0.75;
133         final double m1 = 1 - m;
134         final double u  = 1.3;
135 
136         JacobiElliptic jeD = JacobiEllipticBuilder.build(m);
137         CopolarN valuesND = jeD.valuesN(u);
138         CopolarC valuesCD = jeD.valuesC(u);
139         CopolarS valuesSD = jeD.valuesS(u);
140         CopolarD valuesDD = jeD.valuesD(u);
141 
142         FieldJacobiElliptic<UnivariateDerivative1> jeU = JacobiEllipticBuilder.build(new UnivariateDerivative1(m, 0.0));
143         FieldCopolarN<UnivariateDerivative1> valuesNU = jeU.valuesN(new UnivariateDerivative1(u, 1.0));
144         FieldCopolarC<UnivariateDerivative1> valuesCU = jeU.valuesC(new UnivariateDerivative1(u, 1.0));
145         FieldCopolarS<UnivariateDerivative1> valuesSU = jeU.valuesS(new UnivariateDerivative1(u, 1.0));
146         FieldCopolarD<UnivariateDerivative1> valuesDU = jeU.valuesD(new UnivariateDerivative1(u, 1.0));
147 
148         // see Abramowitz and Stegun section 16.16
149         Assert.assertEquals(      valuesND.cn() * valuesND.dn(), valuesNU.sn().getFirstDerivative(), 2.0e-15);
150         Assert.assertEquals(-1  * valuesND.sn() * valuesND.dn(), valuesNU.cn().getFirstDerivative(), 2.0e-15);
151         Assert.assertEquals(-m  * valuesND.sn() * valuesND.cn(), valuesNU.dn().getFirstDerivative(), 2.0e-15);
152 
153         Assert.assertEquals(-m1 * valuesDD.sd() * valuesDD.nd(), valuesDU.cd().getFirstDerivative(), 2.0e-15);
154         Assert.assertEquals(      valuesDD.cd() * valuesDD.nd(), valuesDU.sd().getFirstDerivative(), 2.0e-15);
155         Assert.assertEquals( m  * valuesDD.sd() * valuesDD.cd(), valuesDU.nd().getFirstDerivative(), 2.0e-15);
156 
157         Assert.assertEquals( m1 * valuesCD.sc() * valuesCD.nc(), valuesCU.dc().getFirstDerivative(), 2.0e-15);
158         Assert.assertEquals(      valuesCD.sc() * valuesCD.dc(), valuesCU.nc().getFirstDerivative(), 2.0e-15);
159         Assert.assertEquals(      valuesCD.dc() * valuesCD.nc(), valuesCU.sc().getFirstDerivative(), 2.0e-15);
160 
161         Assert.assertEquals(-1  * valuesSD.ds() * valuesSD.cs(), valuesSU.ns().getFirstDerivative(), 2.0e-15);
162         Assert.assertEquals(-1  * valuesSD.cs() * valuesSD.ns(), valuesSU.ds().getFirstDerivative(), 2.0e-15);
163         Assert.assertEquals(-1  * valuesSD.ns() * valuesSD.ds(), valuesSU.cs().getFirstDerivative(), 2.0e-15);
164 
165     }
166 
167     private <T extends CalculusFieldElement<T>> void doTestCircular(final Field<T> field) {
168         for (double m : new double[] { -1.0e-10, 0.0, 1.0e-10 }) {
169          final double eps = 3 * FastMath.max(1.0e-14, FastMath.abs(m));
170             final FieldJacobiElliptic<T> je = build(field, m);
171             for (double t = -10; t < 10; t += 0.01) {
172                 final FieldCopolarN<T> n = je.valuesN(t);
173                 Assert.assertEquals(FastMath.sin(t), n.sn().getReal(), eps);
174                 Assert.assertEquals(FastMath.cos(t), n.cn().getReal(), eps);
175                 Assert.assertEquals(1.0,             n.dn().getReal(), eps);
176             }
177         }
178     }
179 
180     private <T extends CalculusFieldElement<T>> void doTestHyperbolic(final Field<T> field) {
181         for (double m1 : new double[] { -1.0e-12, 0.0, 1.0e-12 }) {
182             final double eps = 3 * FastMath.max(1.0e-14, FastMath.abs(m1));
183             final FieldJacobiElliptic<T> je = build(field, 1.0 - m1);
184             for (double t = -3; t < 3; t += 0.01) {
185                 final FieldCopolarN<T> n = je.valuesN(t);
186                 Assert.assertEquals(FastMath.tanh(t),       n.sn().getReal(), eps);
187                 Assert.assertEquals(1.0 / FastMath.cosh(t), n.cn().getReal(), eps);
188                 Assert.assertEquals(1.0 / FastMath.cosh(t), n.dn().getReal(), eps);
189             }
190         }
191     }
192 
193     private <T extends CalculusFieldElement<T>> void doTestNoConvergence(final Field<T> field) {
194         Assert.assertTrue(build(field, Double.NaN).valuesS(0.0).cs().isNaN());
195     }
196 
197     private <T extends CalculusFieldElement<T>> void doTestNegativeParameter(final Field<T> field) {
198         Assert.assertEquals(0.49781366219021166315, build(field, -4.5).valuesN(8.3).sn().getReal(), 1.5e-10);
199         Assert.assertEquals(0.86728401215332559984, build(field, -4.5).valuesN(8.3).cn().getReal(), 1.5e-10);
200         Assert.assertEquals(1.45436686918553524215, build(field, -4.5).valuesN(8.3).dn().getReal(), 1.5e-10);
201     }
202 
203     private <T extends CalculusFieldElement<T>> void doTestAbramowitzStegunExample1(final Field<T> field) {
204         // Abramowitz and Stegun give a result of -1667, but Wolfram Alpha gives the following value
205         Assert.assertEquals(-1392.11114434139393839735, build(field, 0.64).valuesC(1.99650).nc().getReal(), 2.8e-10);
206     }
207 
208     private <T extends CalculusFieldElement<T>> void doTestAbramowitzStegunExample2(final Field<T> field) {
209         Assert.assertEquals(0.996253, build(field, 0.19).valuesN(0.20).dn().getReal(), 1.0e-6);
210     }
211 
212     private <T extends CalculusFieldElement<T>> void doTestAbramowitzStegunExample3(final Field<T> field) {
213         Assert.assertEquals(0.984056, build(field, 0.81).valuesN(0.20).dn().getReal(), 1.0e-6);
214     }
215 
216     private <T extends CalculusFieldElement<T>> void doTestAbramowitzStegunExample4(final Field<T> field) {
217         Assert.assertEquals(0.980278, build(field, 0.81).valuesN(0.20).cn().getReal(), 1.0e-6);
218     }
219 
220     private <T extends CalculusFieldElement<T>> void doTestAbramowitzStegunExample5(final Field<T> field) {
221         Assert.assertEquals(0.60952, build(field, 0.36).valuesN(0.672).sn().getReal(), 1.0e-5);
222         Assert.assertEquals(1.1740, build(field, 0.36).valuesC(0.672).dc().getReal(), 1.0e-4);
223     }
224 
225     private <T extends CalculusFieldElement<T>> void doTestAbramowitzStegunExample7(final Field<T> field) {
226         Assert.assertEquals(1.6918083, build(field, 0.09).valuesS(0.5360162).cs().getReal(), 1.0e-7);
227     }
228 
229     private <T extends CalculusFieldElement<T>> void doTestAbramowitzStegunExample8(final Field<T> field) {
230         Assert.assertEquals(0.56458, build(field, 0.5).valuesN(0.61802).sn().getReal(), 1.0e-5);
231     }
232 
233     private <T extends CalculusFieldElement<T>> void doTestAbramowitzStegunExample9(final Field<T> field) {
234         Assert.assertEquals(0.68402, build(field, 0.5).valuesC(0.61802).sc().getReal(), 1.0e-5);
235     }
236 
237     private <T extends CalculusFieldElement<T>> void doTestAllFunctions(final Field<T> field) {
238         // reference was computed from Wolfram Alpha, using the square relations
239         // from Abramowitz and Stegun section 16.9 for the functions Wolfram Alpha
240         // did not understood (i.e. for the sake of validation we did *not* use the
241         // relations from section 16.3 which are used in the Hipparchus implementation)
242         final double u = 1.4;
243         final double m = 0.7;
244         final double[] reference = {
245               0.92516138673582827365, 0.37957398289798418747, 0.63312991237590996850,
246               0.41027866958131945027, 0.68434537093007175683, 1.08089249544689572795,
247               1.66800134071905681841, 2.63453251554589286796, 2.43736775548306830513,
248               1.57945467502452678756, 1.46125047743207819361, 0.59951990180590090343
249         };
250         final FieldJacobiElliptic<T> je = build(field, m);
251         Assert.assertEquals(reference[ 0], je.valuesN(u).sn().getReal(), 4 * FastMath.ulp(reference[ 0]));
252         Assert.assertEquals(reference[ 1], je.valuesN(u).cn().getReal(), 4 * FastMath.ulp(reference[ 1]));
253         Assert.assertEquals(reference[ 2], je.valuesN(u).dn().getReal(), 4 * FastMath.ulp(reference[ 2]));
254         Assert.assertEquals(reference[ 3], je.valuesS(u).cs().getReal(), 4 * FastMath.ulp(reference[ 3]));
255         Assert.assertEquals(reference[ 4], je.valuesS(u).ds().getReal(), 4 * FastMath.ulp(reference[ 4]));
256         Assert.assertEquals(reference[ 5], je.valuesS(u).ns().getReal(), 4 * FastMath.ulp(reference[ 5]));
257         Assert.assertEquals(reference[ 6], je.valuesC(u).dc().getReal(), 4 * FastMath.ulp(reference[ 6]));
258         Assert.assertEquals(reference[ 7], je.valuesC(u).nc().getReal(), 4 * FastMath.ulp(reference[ 7]));
259         Assert.assertEquals(reference[ 8], je.valuesC(u).sc().getReal(), 4 * FastMath.ulp(reference[ 8]));
260         Assert.assertEquals(reference[ 9], je.valuesD(u).nd().getReal(), 4 * FastMath.ulp(reference[ 9]));
261         Assert.assertEquals(reference[10], je.valuesD(u).sd().getReal(), 4 * FastMath.ulp(reference[10]));
262         Assert.assertEquals(reference[11], je.valuesD(u).cd().getReal(), 4 * FastMath.ulp(reference[11]));
263     }
264 
265     private <T extends CalculusFieldElement<T>> FieldJacobiElliptic<T> build(final Field<T> field, final double m) {
266         return JacobiEllipticBuilder.build(field.getZero().newInstance(m));
267     }
268 
269     private <T extends CalculusFieldElement<T>> void doTestInverseCopolarN(final Field<T> field) {
270         final double m = 0.7;
271         final FieldJacobiElliptic<T> je = build(field, m);
272         doTestInverse(-0.80,  0.80, 100, field, u -> je.valuesN(u).sn(), x -> je.arcsn(x), x -> je.arcsn(x), 1.0e-14);
273         doTestInverse(-1.00,  1.00, 100, field, u -> je.valuesN(u).cn(), x -> je.arccn(x), x -> je.arccn(x), 1.0e-14);
274         doTestInverse( 0.55,  1.00, 100, field, u -> je.valuesN(u).dn(), x -> je.arcdn(x), x -> je.arcdn(x), 1.0e-14);
275     }
276 
277     private <T extends CalculusFieldElement<T>> void doTestInverseCopolarS(final Field<T> field) {
278         final double m = 0.7;
279         final FieldJacobiElliptic<T> je = build(field, m);
280         doTestInverse(-2.00,  2.00, 100, field, u -> je.valuesS(u).cs(), x -> je.arccs(x), x -> je.arccs(x), 1.0e-14);
281         doTestInverse( 0.55,  2.00, 100, field, u -> je.valuesS(u).ds(), x -> je.arcds(x), x -> je.arcds(x), 1.0e-14);
282         doTestInverse(-2.00, -0.55, 100, field, u -> je.valuesS(u).ds(), x -> je.arcds(x), x -> je.arcds(x), 1.0e-14);
283         doTestInverse( 1.00,  2.00, 100, field, u -> je.valuesS(u).ns(), x -> je.arcns(x), x -> je.arcns(x), 1.0e-11);
284         doTestInverse(-2.00, -1.00, 100, field, u -> je.valuesS(u).ns(), x -> je.arcns(x), x -> je.arcns(x), 1.0e-11);
285     }
286 
287     private <T extends CalculusFieldElement<T>> void doTestInverseCopolarC(final Field<T> field) {
288         final double m = 0.7;
289         final FieldJacobiElliptic<T> je = build(field, m);
290         doTestInverse( 1.00,  2.00, 100, field, u -> je.valuesC(u).dc(), x -> je.arcdc(x), x -> je.arcdc(x), 1.0e-14);
291         doTestInverse(-2.00, -1.00, 100, field, u -> je.valuesC(u).dc(), x -> je.arcdc(x), x -> je.arcdc(x), 1.0e-14);
292         doTestInverse( 1.00,  2.00, 100, field, u -> je.valuesC(u).nc(), x -> je.arcnc(x), x -> je.arcnc(x), 1.0e-14);
293         doTestInverse(-2.00, -1.00, 100, field, u -> je.valuesC(u).nc(), x -> je.arcnc(x), x -> je.arcnc(x), 1.0e-14);
294         doTestInverse(-2.00,  2.00, 100, field, u -> je.valuesC(u).sc(), x -> je.arcsc(x), x -> je.arcsc(x), 1.0e-14);
295     }
296 
297     private <T extends CalculusFieldElement<T>> void doTestInverseCopolarD(final Field<T> field) {
298         final double m = 0.7;
299         final FieldJacobiElliptic<T> je = build(field, m);
300         doTestInverse( 1.00,  1.80, 100, field, u -> je.valuesD(u).nd(), x -> je.arcnd(x), x -> je.arcnd(x), 1.0e-14);
301         doTestInverse(-1.80,  1.80, 100, field, u -> je.valuesD(u).sd(), x -> je.arcsd(x), x -> je.arcsd(x), 1.0e-14);
302         doTestInverse(-1.00,  1.00, 100, field, u -> je.valuesD(u).cd(), x -> je.arccd(x), x -> je.arccd(x), 1.0e-14);
303     }
304 
305     private <T extends CalculusFieldElement<T>> void doTestInverse(final double xMin, final double xMax, final int n,
306                                                                    final Field<T> field,
307                                                                    final Function<T, T> direct,
308                                                                    final Function<T, T> inverseField,
309                                                                    final DoubleFunction<T> inverseDouble,
310                                                                    final double tolerance) {
311         for (int i = 0; i < n; ++i) {
312             final T x             = field.getZero().newInstance(xMin + i * (xMax - xMin) / (n - 1));
313             final T xFieldRebuilt = direct.apply(inverseField.apply(x));
314             Assert.assertEquals(x.getReal(), xFieldRebuilt.getReal(), tolerance);
315             final T xDoubleRebuilt = direct.apply(inverseDouble.apply(x.getReal()));
316             Assert.assertEquals(x.getReal(), xDoubleRebuilt.getReal(), tolerance);
317         }
318     }
319 
320 }