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  
18  package org.hipparchus.analysis.differentiation;
19  
20  import org.hipparchus.Field;
21  import org.hipparchus.UnitTestUtils;
22  import org.hipparchus.complex.Complex;
23  import org.hipparchus.complex.ComplexField;
24  import org.hipparchus.util.Binary64;
25  import org.hipparchus.util.FastMath;
26  import org.hipparchus.util.MathUtils;
27  import org.hipparchus.util.Precision;
28  import org.junit.jupiter.api.Test;
29  
30  import java.util.Arrays;
31  import java.util.List;
32  
33  import static org.junit.jupiter.api.Assertions.assertEquals;
34  import static org.junit.jupiter.api.Assertions.assertFalse;
35  import static org.junit.jupiter.api.Assertions.assertTrue;
36  
37  /**
38   * Test for class {@link FieldDerivativeStructure} on {@link Complex}.
39   */
40  class FieldDerivativeStructureComplexTest extends FieldDerivativeStructureAbstractTest<Complex> {
41  
42      @Override
43      protected Field<Complex> getField() {
44          return ComplexField.getInstance();
45      }
46  
47      @Override
48      @Test
49      public void testComposeField() {
50          doTestComposeField(new double[] { 1.0e-100, 5.0e-14, 2.0e-13, 3.0e-13, 2.0e-13, 1.0e-100 });
51      }
52  
53      @Override
54      @Test
55      public void testComposePrimitive() {
56          doTestComposePrimitive(new double[] { 1.0e-100, 5.0e-14, 2.0e-13, 3.0e-13, 2.0e-13, 1.0e-100 });
57      }
58  
59      @Override
60      @Test
61      public void testHypotNoOverflow() {
62          doTestHypotNoOverflow(180);
63      }
64  
65      @Override
66      @Test
67      public void testLinearCombinationReference() {
68          doTestLinearCombinationReference(x -> build(x), 4.8e-16, 1.0);
69      }
70  
71      @Override
72      @Test
73      public void testLinearCombination1DSDS() {
74          doTestLinearCombination1DSDS(1.0e-15);
75      }
76  
77      @Override
78      @Test
79      public void testLinearCombination1FieldDS() {
80          doTestLinearCombination1FieldDS(1.0e-15);
81      }
82  
83      @Override
84      @Test
85      public void testLinearCombination1DoubleDS() {
86          doTestLinearCombination1DoubleDS(1.0e-15);
87      }
88  
89      @Override
90      @Test
91      public void testAtan2() {
92          double[] epsilon = new double[] { 9.0e-16, 3.0e-15, 2.9e-14, 1.0e-12, 8.0e-11 };
93          for (int maxOrder = 0; maxOrder < 5; ++maxOrder) {
94              final FDSFactory<Complex> factory = buildFactory(2, maxOrder);
95              for (double x = -1.7; x < 2; x += 0.2) {
96                  FieldDerivativeStructure<Complex> dsX = factory.variable(0, x);
97                  for (double y = -1.7; y < 2; y += 0.2) {
98                      FieldDerivativeStructure<Complex> dsY = factory.variable(1, y);
99                      FieldDerivativeStructure<Complex> atan2 = FieldDerivativeStructure.atan2(dsY, dsX);
100                     FieldDerivativeStructure<Complex> ref = dsY.divide(dsX).atan();
101                     if (x < 0) {
102                         ref = (y < 0) ? ref.subtract(FastMath.PI) : ref.add(FastMath.PI);
103                     }
104                     double fullTurns = MathUtils.normalizeAngle(atan2.getValue().getReal(), ref.getValue().getReal()) - atan2.getValue().getReal();
105                     atan2 = atan2.add(fullTurns);
106                     FieldDerivativeStructure<Complex> zero = atan2.subtract(ref);
107                     for (int n = 0; n <= maxOrder; ++n) {
108                         for (int m = 0; m <= maxOrder; ++m) {
109                             if (n + m <= maxOrder) {
110                                 assertEquals(0, zero.getPartialDerivative(n, m).getReal(), epsilon[n + m]);
111                             }
112                         }
113                     }
114                 }
115             }
116         }
117     }
118 
119     @Override
120     @Test
121     public void testAtan2SpecialCases() {
122         assertTrue(build(+0.0).atan2(build(+0.0)).isNaN());
123         assertTrue(build(-0.0).atan2(build(+0.0)).isNaN());
124         assertTrue(build(+0.0).atan2(build(-0.0)).isNaN());
125         assertTrue(build(-0.0).atan2(build(-0.0)).isNaN());
126     }
127 
128     @Test
129     public void testAtan2SpecialCasesDerivatives() {
130 
131         final FDSFactory<Complex> factory = buildFactory(2, 2);
132         FieldDerivativeStructure<Complex> pp =
133                 FieldDerivativeStructure.atan2(factory.variable(1, buildScalar(+0.0)), factory.variable(1, buildScalar(+0.0)));
134         assertTrue(pp.getValue().isNaN());
135 
136         FieldDerivativeStructure<Complex> pn =
137                 FieldDerivativeStructure.atan2(factory.variable(1, buildScalar(+0.0)), factory.variable(1, buildScalar(-0.0)));
138         assertTrue(pn.getValue().isNaN());
139 
140         FieldDerivativeStructure<Complex> np =
141                 FieldDerivativeStructure.atan2(factory.variable(1, buildScalar(-0.0)), factory.variable(1, buildScalar(+0.0)));
142         assertTrue(np.getValue().isNaN());
143 
144         FieldDerivativeStructure<Complex> nn =
145                 FieldDerivativeStructure.atan2(factory.variable(1, buildScalar(-0.0)), factory.variable(1, buildScalar(-0.0)));
146         assertTrue(nn.getValue().isNaN());
147 
148     }
149 
150     @Override
151     @Test
152     public void testPowDoubleDS() {
153         for (int maxOrder = 1; maxOrder < 5; ++maxOrder) {
154 
155             final FDSFactory<Complex> factory = buildFactory(3, maxOrder);
156             FieldDerivativeStructure<Complex> x = factory.variable(0, 0.1);
157             FieldDerivativeStructure<Complex> y = factory.variable(1, 0.2);
158             FieldDerivativeStructure<Complex> z = factory.variable(2, 0.3);
159             List<FieldDerivativeStructure<Complex>> list = Arrays.asList(x, y, z,
160                                                                            x.add(y).add(z),
161                                                                            x.multiply(y).multiply(z));
162 
163             for (FieldDerivativeStructure<Complex> ds : list) {
164                 // the special case a = 0 is included here
165                 for (double a : new double[] { 0.0, 0.1, 1.0, 2.0, 5.0 }) {
166                     FieldDerivativeStructure<Complex> reference = (a == 0) ?
167                                                     x.getField().getZero() :
168                                                     factory.constant(a).pow(ds);
169                     FieldDerivativeStructure<Complex> result = FieldDerivativeStructure.pow(a, ds);
170                     checkEquals(reference, result, 2.0e-14 * FastMath.abs(reference.getReal()));
171                 }
172 
173             }
174 
175             // negative base: -1^x can be evaluated for integers only, so value is sometimes OK, derivatives are always NaN
176             FieldDerivativeStructure<Complex> negEvenInteger = FieldDerivativeStructure.pow(-2.0, factory.variable(0, 2.0));
177             assertEquals(4.0, negEvenInteger.getReal(), 1.0e-15);
178             assertTrue(Double.isNaN(negEvenInteger.getPartialDerivative(1, 0, 0).getReal()));
179             FieldDerivativeStructure<Complex> negOddInteger = FieldDerivativeStructure.pow(-2.0, factory.variable(0, 3.0));
180             assertEquals(-8.0, negOddInteger.getReal(), 1.0e-15);
181             assertTrue(Double.isNaN(negOddInteger.getPartialDerivative(1, 0, 0).getReal()));
182             FieldDerivativeStructure<Complex> negNonInteger = FieldDerivativeStructure.pow(-2.0, factory.variable(0, 2.001));
183             assertEquals(4.0027537969708465469, negNonInteger.getValue().getReal(), 2.0e-15);
184             assertEquals(0.012575063293019489803, negNonInteger.getValue().getImaginary(), 2.0e-15);
185             assertTrue(Double.isNaN(negNonInteger.getPartialDerivative(1, 0, 0).getReal()));
186 
187             FieldDerivativeStructure<Complex> zeroNeg = FieldDerivativeStructure.pow(0.0, factory.variable(0, -1.0));
188             assertTrue(Double.isNaN(zeroNeg.getReal()));
189             assertTrue(Double.isNaN(zeroNeg.getPartialDerivative(1, 0, 0).getReal()));
190             FieldDerivativeStructure<Complex> posNeg = FieldDerivativeStructure.pow(2.0, factory.variable(0, -2.0));
191             assertEquals(1.0 / 4.0, posNeg.getReal(), 1.0e-15);
192             assertEquals(FastMath.log(2.0) / 4.0, posNeg.getPartialDerivative(1, 0, 0).getReal(), 1.0e-15);
193 
194             // very special case: 0^0 where the power is a primitive
195             FieldDerivativeStructure<Complex> zeroDsZeroDouble = factory.variable(0, 0.0).pow(0.0);
196             boolean first = true;
197             for (final Complex d : zeroDsZeroDouble.getAllDerivatives()) {
198                 if (first) {
199                     assertEquals(1.0, d.getReal(), Precision.EPSILON);
200                     first = false;
201                 } else {
202                     assertEquals(0.0, d.getReal(), Precision.SAFE_MIN);
203                 }
204             }
205             FieldDerivativeStructure<Complex> zeroDsZeroInt = factory.variable(0, 0.0).pow(0);
206             first = true;
207             for (final Complex d : zeroDsZeroInt.getAllDerivatives()) {
208                 if (first) {
209                     assertEquals(1.0, d.getReal(), Precision.EPSILON);
210                     first = false;
211                 } else {
212                     assertEquals(0.0, d.getReal(), Precision.SAFE_MIN);
213                 }
214             }
215 
216             // 0^p with p smaller than 1.0
217             FieldDerivativeStructure<Complex> u = factory.variable(1, -0.0).pow(0.25);
218             for (int i0 = 0; i0 <= maxOrder; ++i0) {
219                 for (int i1 = 0; i1 <= maxOrder; ++i1) {
220                     for (int i2 = 0; i2 <= maxOrder; ++i2) {
221                         if (i0 + i1 + i2 <= maxOrder) {
222                             assertEquals(0.0, u.getPartialDerivative(i0, i1, i2).getReal(), 1.0e-10);
223                         }
224                     }
225                 }
226             }
227         }
228 
229     }
230 
231     @Override
232     @Test
233     public void testLog10() {
234         for (double x = -0.9; x < 0.9; x += 0.05) {
235             if (x <= 0) {
236                 assertTrue(Double.isNaN(FastMath.log10(x)));
237                 assertFalse(build(x).log10().getValue().isNaN());
238             } else {
239                 checkRelative(FastMath.log10(x), build(x).log10());
240             }
241         }
242     }
243 
244     @Override
245     @Test
246     public void testRootN() {
247         for (double x = -0.9; x < 0.9; x += 0.05) {
248             for (int n = 1; n < 5; ++n) {
249                 if (x < 0) {
250                     // special case for Complex
251                     final double doubleRoot = new Binary64(x).rootN(n).getReal();
252                     if (n % 2 == 0) {
253                         assertTrue(Double.isNaN(doubleRoot));
254                     } else {
255                         assertTrue(doubleRoot < 0);
256                     }
257                     assertEquals(FastMath.PI / n, build(x).rootN(n).getValue().getArgument(), 1.0e-15);
258                 } else {
259                     checkRelative(FastMath.pow(x, 1.0 / n), build(x).rootN(n));
260                 }
261             }
262         }
263     }
264 
265     @Override
266     @Test
267     public void testRootNSingularity() {
268         doTestRootNSingularity(false);
269     }
270 
271     @Override
272     @Test
273     public void testCbrt() {
274         for (double x = -0.9; x < 0.9; x += 0.05) {
275             if ( x < 0) {
276                 // special case for Complex
277                 assertTrue(FastMath.cbrt(x) < 0);
278                 assertEquals(FastMath.PI / 3, build(x).cbrt().getValue().getArgument(), 1.0e-15);
279             } else {
280                 checkRelative(FastMath.cbrt(x), build(x).cbrt());
281             }
282         }
283     }
284 
285     @Test
286     void testCbrtComplex() {
287         Complex z = new Complex(15, 2);
288         UnitTestUtils.customAssertEquals(z, z.square().multiply(z).cbrt(), 1.0e-14);
289         Complex branchCutPlus = new Complex(-8.0, +0.0);
290         Complex cbrtPlus = branchCutPlus.cbrt();
291         UnitTestUtils.customAssertEquals(branchCutPlus, cbrtPlus.multiply(cbrtPlus).multiply(cbrtPlus), 1.0e-14);
292         assertEquals(1.0, cbrtPlus.getReal(), 1.0e-15);
293         assertEquals(FastMath.sqrt(3.0), cbrtPlus.getImaginary(), 1.0e-15);
294         Complex branchCutMinus = new Complex(-8.0, -0.0);
295         Complex cbrtMinus = branchCutMinus.cbrt();
296         UnitTestUtils.customAssertEquals(branchCutMinus, cbrtMinus.multiply(cbrtMinus).multiply(cbrtMinus), 1.0e-14);
297         assertEquals(1.0, cbrtMinus.getReal(), 1.0e-15);
298         assertEquals(-FastMath.sqrt(3.0), cbrtMinus.getImaginary(), 1.0e-15);
299     }
300 
301     @Override
302     @Test
303     public void testPowField() {
304         for (double x = -0.9; x < 0.9; x += 0.05) {
305             for (double y = 0.1; y < 4; y += 0.2) {
306                 if ( x < 0) {
307                     // special case for Complex
308                     assertTrue(Double.isNaN(FastMath.pow(x, y)));
309                     assertFalse(build(x).pow(build(y)).isNaN());
310                 } else {
311                     checkRelative(FastMath.pow(x, y), build(x).pow(build(y)));
312                 }
313             }
314         }
315     }
316 
317     @Override
318     @Test
319     public void testPowDouble() {
320         for (double x = -0.9; x < 0.9; x += 0.05) {
321             for (double y = 0.1; y < 4; y += 0.2) {
322                 if ( x < 0) {
323                     // special case for Complex
324                     assertTrue(Double.isNaN(FastMath.pow(x, y)));
325                     assertFalse(build(x).pow(y).isNaN());
326                 } else {
327                     checkRelative(FastMath.pow(x, y), build(x).pow(y));
328                 }
329             }
330         }
331     }
332 
333 }