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