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    *      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  /*
19   * This is not the original file distributed by the Apache Software Foundation
20   * It has been modified by the Hipparchus project
21   */
22  package org.hipparchus.analysis.differentiation;
23  
24  import org.hipparchus.CalculusFieldElementAbstractTest;
25  import org.hipparchus.Field;
26  import org.hipparchus.UnitTestUtils;
27  import org.hipparchus.analysis.polynomials.PolynomialFunction;
28  import org.hipparchus.random.Well1024a;
29  import org.hipparchus.util.FastMath;
30  import org.hipparchus.util.FieldSinCos;
31  import org.junit.jupiter.api.Test;
32  
33  import java.util.ArrayList;
34  import java.util.Arrays;
35  import java.util.HashMap;
36  import java.util.List;
37  import java.util.Map;
38  
39  import static org.junit.jupiter.api.Assertions.assertEquals;
40  import static org.junit.jupiter.api.Assertions.assertNotEquals;
41  import static org.junit.jupiter.api.Assertions.assertTrue;
42  
43  class SparseGradientTest extends CalculusFieldElementAbstractTest<SparseGradient> {
44  
45      @Override
46      protected SparseGradient build(final double x) {
47          return SparseGradient.createVariable(0, x);
48      }
49  
50      @Test
51      void testConstant() {
52          double c = 1.0;
53          SparseGradient grad = SparseGradient.createConstant(c);
54          assertEquals(c, grad.getValue(), 1.0e-15); // returns the value
55          assertEquals(0, grad.getFreeParameters(), 1.0e-15); // has no variables
56      }
57  
58      @Test
59      void testVariable() {
60          double v = 1.0;
61          int id = 0;
62          SparseGradient grad = SparseGradient.createVariable(id, v);
63          assertEquals(v, grad.getValue(), 1.0e-15); // returns the value
64          assertEquals(1, grad.getFreeParameters(), 1.0e-15); // has one variable
65          assertEquals(1.0, grad.getDerivative(id), 1.0e-15); // derivative wr.t itself is 1
66      }
67  
68      @Test
69      void testVarAddition() {
70          final double v1 = 1.0;
71          final double v2 = 2.0;
72          final int id1 = -1;
73          final int id2 = 3;
74          final SparseGradient var1 = SparseGradient.createVariable(id1, v1);
75          final SparseGradient var2 = SparseGradient.createVariable(id2, v2);
76          final SparseGradient sum = var1.add(var2);
77  
78          assertEquals(v1 + v2, sum.getValue(), 1.0e-15); // returns the value
79          assertEquals(2, sum.getFreeParameters());
80          assertEquals(1.0, sum.getDerivative(id1), 1.0e-15);
81          assertEquals(1.0, sum.getDerivative(id2), 1.0e-15);
82      }
83  
84      @Test
85      void testSubtraction() {
86          final double v1 = 1.0;
87          final double v2 = 2.0;
88          final int id1 = -1;
89          final int id2 = 3;
90          final SparseGradient var1 = SparseGradient.createVariable(id1, v1);
91          final SparseGradient var2 = SparseGradient.createVariable(id2, v2);
92          final SparseGradient sum = var1.subtract(var2);
93  
94          assertEquals(v1 - v2, sum.getValue(), 1.0e-15); // returns the value
95          assertEquals(2, sum.getFreeParameters());
96          assertEquals(1.0, sum.getDerivative(id1), 1.0e-15);
97          assertEquals(-1.0, sum.getDerivative(id2), 1.0e-15);
98      }
99  
100     @Test
101     void testDivision() {
102         final double v1 = 1.0;
103         final double v2 = 2.0;
104         final int id1 = -1;
105         final int id2 = 3;
106         final SparseGradient var1 = SparseGradient.createVariable(id1, v1);
107         final SparseGradient var2 = SparseGradient.createVariable(id2, v2);
108         final SparseGradient out = var1.divide(var2);
109         assertEquals(v1 / v2, out.getValue(), 1.0e-15); // returns the value
110         assertEquals(2, out.getFreeParameters());
111         assertEquals(1 / v2, out.getDerivative(id1), 1.0e-15);
112         assertEquals(-1 / (v2 * v2), out.getDerivative(id2), 1.0e-15);
113     }
114 
115     @Test
116     void testMult() {
117         final double v1 = 1.0;
118         final double c1 = 0.5;
119         final double v2 = 2.0;
120         final int id1 = -1;
121         final int id2 = 3;
122         final SparseGradient var1 = SparseGradient.createVariable(id1, v1);
123         final SparseGradient unit1 = var1.multiply(c1);
124         final SparseGradient unit2 = SparseGradient.createVariable(id2, v2).multiply(var1);
125         final SparseGradient sum = unit1.add(unit2);
126         assertEquals(v1 * c1 + v2 * v1, sum.getValue(), 1.0e-15); // returns the value
127         assertEquals(2, sum.getFreeParameters());
128         assertEquals(c1 + v2, sum.getDerivative(id1), 1.0e-15);
129         assertEquals(v1, sum.getDerivative(id2), 1.0e-15);
130     }
131 
132     @Test
133     void testVarMultInPlace() {
134         final double v1 = 1.0;
135         final double c1 = 0.5;
136         final double v2 = 2.0;
137         final int id1 = -1;
138         final int id2 = 3;
139         final SparseGradient var1 = SparseGradient.createVariable(id1, v1);
140         final SparseGradient sum = var1.multiply(c1);
141         final SparseGradient mult = SparseGradient.createVariable(id2, v2);
142         mult.multiplyInPlace(var1);
143         sum.addInPlace(mult);
144         assertEquals(v1 * c1 + v2 * v1, sum.getValue(), 1.0e-15); // returns the value
145         assertEquals(2, sum.getFreeParameters());
146         assertEquals(c1 + v2, sum.getDerivative(id1), 1.0e-15);
147         assertEquals(v1, sum.getDerivative(id2), 1.0e-15);
148     }
149 
150     @Test
151     void testPrimitiveAdd() {
152         checkF0F1(SparseGradient.createVariable(0, 1.0).add(5), 6.0, 1.0, 0.0, 0.0);
153         checkF0F1(SparseGradient.createVariable(1, 2.0).add(5), 7.0, 0.0, 1.0, 0.0);
154         checkF0F1(SparseGradient.createVariable(2, 3.0).add(5), 8.0, 0.0, 0.0, 1.0);
155     }
156 
157     @Test
158     void testAdd() {
159         SparseGradient x = SparseGradient.createVariable(0, 1.0);
160         SparseGradient y = SparseGradient.createVariable(1, 2.0);
161         SparseGradient z = SparseGradient.createVariable(2, 3.0);
162         SparseGradient xyz = x.add(y.add(z));
163         checkF0F1(xyz, x.getValue() + y.getValue() + z.getValue(), 1.0, 1.0, 1.0);
164     }
165 
166     @Test
167     void testPrimitiveSubtract() {
168         checkF0F1(SparseGradient.createVariable(0, 1.0).subtract(5), -4.0, 1.0, 0.0, 0.0);
169         checkF0F1(SparseGradient.createVariable(1, 2.0).subtract(5), -3.0, 0.0, 1.0, 0.0);
170         checkF0F1(SparseGradient.createVariable(2, 3.0).subtract(5), -2.0, 0.0, 0.0, 1.0);
171     }
172 
173     @Test
174     void testSubtract() {
175         SparseGradient x = SparseGradient.createVariable(0, 1.0);
176         SparseGradient y = SparseGradient.createVariable(1, 2.0);
177         SparseGradient z = SparseGradient.createVariable(2, 3.0);
178         SparseGradient xyz = x.subtract(y.subtract(z));
179         checkF0F1(xyz, x.getValue() - (y.getValue() - z.getValue()), 1.0, -1.0, 1.0);
180     }
181 
182     @Test
183     void testPrimitiveMultiply() {
184         checkF0F1(SparseGradient.createVariable(0, 1.0).multiply(5),  5.0, 5.0, 0.0, 0.0);
185         checkF0F1(SparseGradient.createVariable(1, 2.0).multiply(5), 10.0, 0.0, 5.0, 0.0);
186         checkF0F1(SparseGradient.createVariable(2, 3.0).multiply(5), 15.0, 0.0, 0.0, 5.0);
187     }
188 
189     @Test
190     void testMultiply() {
191         SparseGradient x = SparseGradient.createVariable(0, 1.0);
192         SparseGradient y = SparseGradient.createVariable(1, 2.0);
193         SparseGradient z = SparseGradient.createVariable(2, 3.0);
194         SparseGradient xyz = x.multiply(y.multiply(z));
195         checkF0F1(xyz, 6.0, 6.0, 3.0, 2.0);
196     }
197 
198     @Test
199     void testNegate() {
200         checkF0F1(SparseGradient.createVariable(0, 1.0).negate(), -1.0, -1.0, 0.0, 0.0);
201         checkF0F1(SparseGradient.createVariable(1, 2.0).negate(), -2.0, 0.0, -1.0, 0.0);
202         checkF0F1(SparseGradient.createVariable(2, 3.0).negate(), -3.0, 0.0, 0.0, -1.0);
203     }
204 
205     @Test
206     void testReciprocal() {
207         for (double x = 0.1; x < 1.2; x += 0.1) {
208             SparseGradient r = SparseGradient.createVariable(0, x).reciprocal();
209             assertEquals(1 / x, r.getValue(), 1.0e-15);
210             final double expected = -1 / (x * x);
211             assertEquals(expected, r.getDerivative(0), 1.0e-15 * FastMath.abs(expected));
212         }
213     }
214 
215     @Test
216     void testPow() {
217         for (int n = 0; n < 10; ++n) {
218 
219             SparseGradient x = SparseGradient.createVariable(0, 1.0);
220             SparseGradient y = SparseGradient.createVariable(1, 2.0);
221             SparseGradient z = SparseGradient.createVariable(2, 3.0);
222             List<SparseGradient> list = Arrays.asList(x, y, z,
223                                                       x.add(y).add(z),
224                                                       x.multiply(y).multiply(z));
225 
226             if (n == 0) {
227                 for (SparseGradient sg : list) {
228                     assertEquals(sg.getField().getOne(), sg.pow(n));
229                 }
230             } else if (n == 1) {
231                 for (SparseGradient sg : list) {
232                     assertEquals(sg, sg.pow(n));
233                 }
234             } else {
235                 for (SparseGradient sg : list) {
236                     SparseGradient p = sg.getField().getOne();
237                     for (int i = 0; i < n; ++i) {
238                         p = p.multiply(sg);
239                     }
240                     assertEquals(p, sg.pow(n));
241                 }
242             }
243         }
244     }
245 
246     @Test
247     void testPowDoubleDS() {
248         for (int maxOrder = 1; maxOrder < 5; ++maxOrder) {
249 
250             SparseGradient x = SparseGradient.createVariable(0, 0.1);
251             SparseGradient y = SparseGradient.createVariable(1, 0.2);
252             SparseGradient z = SparseGradient.createVariable(2, 0.3);
253             List<SparseGradient> list = Arrays.asList(x, y, z,
254                                                       x.add(y).add(z),
255                                                       x.multiply(y).multiply(z));
256 
257             for (SparseGradient sg : list) {
258                 // the special case a = 0 is included here
259                 for (double a : new double[] { 0.0, 0.1, 1.0, 2.0, 5.0 }) {
260                     SparseGradient reference = (a == 0) ?
261                                                x.getField().getZero() :
262                                                SparseGradient.createConstant(a).pow(sg);
263                     SparseGradient result = SparseGradient.pow(a, sg);
264                     assertEquals(reference, result);
265                 }
266 
267             }
268 
269             // negative base: -1^x can be evaluated for integers only, so value is sometimes OK, derivatives are always NaN
270             SparseGradient negEvenInteger = SparseGradient.pow(-2.0, SparseGradient.createVariable(0, 2.0));
271             assertEquals(4.0, negEvenInteger.getValue(), 1.0e-15);
272             assertTrue(Double.isNaN(negEvenInteger.getDerivative(0)));
273             SparseGradient negOddInteger = SparseGradient.pow(-2.0, SparseGradient.createVariable(0, 3.0));
274             assertEquals(-8.0, negOddInteger.getValue(), 1.0e-15);
275             assertTrue(Double.isNaN(negOddInteger.getDerivative(0)));
276             SparseGradient negNonInteger = SparseGradient.pow(-2.0, SparseGradient.createVariable(0, 2.001));
277             assertTrue(Double.isNaN(negNonInteger.getValue()));
278             assertTrue(Double.isNaN(negNonInteger.getDerivative(0)));
279 
280             SparseGradient zeroNeg = SparseGradient.pow(0.0, SparseGradient.createVariable(0, -1.0));
281             assertTrue(Double.isNaN(zeroNeg.getValue()));
282             assertTrue(Double.isNaN(zeroNeg.getDerivative(0)));
283             SparseGradient posNeg = SparseGradient.pow(2.0, SparseGradient.createVariable(0, -2.0));
284             assertEquals(1.0 / 4.0, posNeg.getValue(), 1.0e-15);
285             assertEquals(FastMath.log(2.0) / 4.0, posNeg.getDerivative(0), 1.0e-15);
286 
287             // very special case: a = 0 and power = 0
288             SparseGradient zeroZero = SparseGradient.pow(0.0, SparseGradient.createVariable(0, 0.0));
289 
290             // this should be OK for simple first derivative with one variable only ...
291             assertEquals(1.0, zeroZero.getValue(), 1.0e-15);
292             assertEquals(Double.NEGATIVE_INFINITY, zeroZero.getDerivative(0), 1.0e-15);
293             assertEquals(0.0, zeroZero.getDerivative(1), 1.0e-15);
294             assertEquals(0.0, zeroZero.getDerivative(2), 1.0e-15);
295 
296         }
297 
298     }
299 
300     @Test
301     void testExpression() {
302         double epsilon = 2.5e-13;
303         for (double x = 0; x < 2; x += 0.2) {
304             SparseGradient sgX = SparseGradient.createVariable(0, x);
305             for (double y = 0; y < 2; y += 0.2) {
306                 SparseGradient sgY = SparseGradient.createVariable(1, y);
307                 for (double z = 0; z >- 2; z -= 0.2) {
308                     SparseGradient sgZ = SparseGradient.createVariable(2, z);
309 
310                     // f(x, y, z) = x + 5 x y - 2 z + (8 z x - y)^3
311                     SparseGradient sg =
312                             sgZ.linearCombination(1, sgX,
313                                                   5, sgX.multiply(sgY),
314                                                  -2, sgZ,
315                                                  1, sgZ.linearCombination(8, sgZ.multiply(sgX), -1, sgY).pow(3));
316                     double f = x + 5 * x * y - 2 * z + FastMath.pow(8 * z * x - y, 3);
317                     assertEquals(f, sg.getValue(), FastMath.abs(epsilon * f));
318 
319                     // df/dx = 1 + 5 y + 24 (8 z x - y)^2 z
320                     double dfdx = 1 + 5 * y + 24 * z * FastMath.pow(8 * z * x - y, 2);
321                     assertEquals(dfdx, sg.getDerivative(0), FastMath.abs(epsilon * dfdx));
322 
323                 }
324 
325             }
326         }
327     }
328 
329     @Test
330     void testCompositionOneVariableX() {
331         double epsilon = 1.0e-13;
332         for (double x = 0.1; x < 1.2; x += 0.1) {
333             SparseGradient sgX = SparseGradient.createVariable(0, x);
334             for (double y = 0.1; y < 1.2; y += 0.1) {
335                 SparseGradient sgY = SparseGradient.createConstant(y);
336                 SparseGradient f = sgX.divide(sgY).sqrt();
337                 double f0 = FastMath.sqrt(x / y);
338                 assertEquals(f0, f.getValue(), FastMath.abs(epsilon * f0));
339                 double f1 = 1 / (2 * FastMath.sqrt(x * y));
340                 assertEquals(f1, f.getDerivative(0), FastMath.abs(epsilon * f1));
341             }
342         }
343     }
344 
345     @Test
346     void testTrigo() {
347         double epsilon = 2.0e-12;
348             for (double x = 0.1; x < 1.2; x += 0.1) {
349                 SparseGradient sgX = SparseGradient.createVariable(0, x);
350                 for (double y = 0.1; y < 1.2; y += 0.1) {
351                     SparseGradient sgY = SparseGradient.createVariable(1, y);
352                     for (double z = 0.1; z < 1.2; z += 0.1) {
353                         SparseGradient sgZ = SparseGradient.createVariable(2, z);
354                         SparseGradient f = sgX.divide(sgY.cos().add(sgZ.tan())).sin();
355                         double a = FastMath.cos(y) + FastMath.tan(z);
356                         double f0 = FastMath.sin(x / a);
357                         assertEquals(f0, f.getValue(), FastMath.abs(epsilon * f0));
358                         double dfdx = FastMath.cos(x / a) / a;
359                         assertEquals(dfdx, f.getDerivative(0), FastMath.abs(epsilon * dfdx));
360                         double dfdy =  x * FastMath.sin(y) * dfdx / a;
361                         assertEquals(dfdy, f.getDerivative(1), FastMath.abs(epsilon * dfdy));
362                         double cz = FastMath.cos(z);
363                         double cz2 = cz * cz;
364                         double dfdz = -x * dfdx / (a * cz2);
365                         assertEquals(dfdz, f.getDerivative(2), FastMath.abs(epsilon * dfdz));
366                     }
367                 }
368             }
369     }
370 
371     @Test
372     void testSqrtDefinition() {
373         for (double x = 0.1; x < 1.2; x += 0.001) {
374             SparseGradient sgX = SparseGradient.createVariable(0, x);
375             SparseGradient sqrt1 = sgX.pow(0.5);
376             SparseGradient sqrt2 = sgX.sqrt();
377             SparseGradient zero = sqrt1.subtract(sqrt2);
378             checkF0F1(zero, 0.0, 0.0);
379         }
380     }
381 
382     @Test
383     void testRootNSingularity() {
384         for (int n = 2; n < 10; ++n) {
385             SparseGradient sgZero = SparseGradient.createVariable(0, 0.0);
386             SparseGradient rootN  = sgZero.rootN(n);
387             assertEquals(0.0, rootN.getValue(), 1.0e-5);
388             assertTrue(Double.isInfinite(rootN.getDerivative(0)));
389             assertTrue(rootN.getDerivative(0) > 0);
390         }
391 
392     }
393 
394     @Test
395     void testSqrtPow2() {
396         for (double x = 0.1; x < 1.2; x += 0.001) {
397             SparseGradient sgX = SparseGradient.createVariable(0, x);
398             SparseGradient rebuiltX = sgX.multiply(sgX).sqrt();
399             SparseGradient zero = rebuiltX.subtract(sgX);
400             checkF0F1(zero, 0.0, 0.0);
401         }
402     }
403 
404     @Test
405     void testCbrtDefinition() {
406         for (double x = 0.1; x < 1.2; x += 0.001) {
407             SparseGradient sgX = SparseGradient.createVariable(0, x);
408             SparseGradient cbrt1 = sgX.pow(1.0 / 3.0);
409             SparseGradient cbrt2 = sgX.cbrt();
410             SparseGradient zero = cbrt1.subtract(cbrt2);
411             checkF0F1(zero, 0.0, 0.0);
412         }
413     }
414 
415     @Test
416     void testCbrtPow3() {
417         for (double x = 0.1; x < 1.2; x += 0.001) {
418             SparseGradient sgX = SparseGradient.createVariable(0, x);
419             SparseGradient rebuiltX = sgX.multiply(sgX.multiply(sgX)).cbrt();
420             SparseGradient zero = rebuiltX.subtract(sgX);
421             checkF0F1(zero, 0.0, 0.0);
422         }
423     }
424 
425     @Test
426     void testPowReciprocalPow() {
427         for (double x = 0.1; x < 1.2; x += 0.01) {
428             SparseGradient sgX = SparseGradient.createVariable(0, x);
429             for (double y = 0.1; y < 1.2; y += 0.01) {
430                 SparseGradient sgY = SparseGradient.createVariable(1, y);
431                 SparseGradient rebuiltX = sgX.pow(sgY).pow(sgY.reciprocal());
432                 SparseGradient zero = rebuiltX.subtract(sgX);
433                 checkF0F1(zero, 0.0, 0.0, 0.0);
434             }
435         }
436     }
437 
438     @Test
439     void testHypotDefinition() {
440         for (double x = -1.7; x < 2; x += 0.2) {
441             SparseGradient sgX = SparseGradient.createVariable(0, x);
442             for (double y = -1.7; y < 2; y += 0.2) {
443                 SparseGradient sgY = SparseGradient.createVariable(1, y);
444                 SparseGradient hypot = SparseGradient.hypot(sgY, sgX);
445                 SparseGradient ref = sgX.multiply(sgX).add(sgY.multiply(sgY)).sqrt();
446                 SparseGradient zero = hypot.subtract(ref);
447                 checkF0F1(zero, 0.0, 0.0, 0.0);
448 
449             }
450         }
451     }
452 
453     @Test
454     void testHypotNoOverflow() {
455 
456         SparseGradient sgX = SparseGradient.createVariable(0, +3.0e250);
457         SparseGradient sgY = SparseGradient.createVariable(1, -4.0e250);
458         SparseGradient hypot = SparseGradient.hypot(sgX, sgY);
459         assertEquals(5.0e250, hypot.getValue(), 1.0e235);
460         assertEquals(sgX.getValue() / hypot.getValue(), hypot.getDerivative(0), 1.0e-10);
461         assertEquals(sgY.getValue() / hypot.getValue(), hypot.getDerivative(1), 1.0e-10);
462 
463         SparseGradient sqrt  = sgX.multiply(sgX).add(sgY.multiply(sgY)).sqrt();
464         assertTrue(Double.isInfinite(sqrt.getValue()));
465 
466     }
467 
468     @Test
469     void testHypotNeglectible() {
470 
471         SparseGradient sgSmall = SparseGradient.createVariable(0, +3.0e-10);
472         SparseGradient sgLarge = SparseGradient.createVariable(1, -4.0e25);
473 
474         assertEquals(sgLarge.abs().getValue(),
475                             SparseGradient.hypot(sgSmall, sgLarge).getValue(),
476                             1.0e-10);
477         assertEquals(0,
478                             SparseGradient.hypot(sgSmall, sgLarge).getDerivative(0),
479                             1.0e-10);
480         assertEquals(-1,
481                             SparseGradient.hypot(sgSmall, sgLarge).getDerivative(1),
482                             1.0e-10);
483 
484         assertEquals(sgLarge.abs().getValue(),
485                             SparseGradient.hypot(sgLarge, sgSmall).getValue(),
486                             1.0e-10);
487         assertEquals(0,
488                             SparseGradient.hypot(sgLarge, sgSmall).getDerivative(0),
489                             1.0e-10);
490         assertEquals(-1,
491                             SparseGradient.hypot(sgLarge, sgSmall).getDerivative(1),
492                             1.0e-10);
493 
494     }
495 
496     @Test
497     void testHypotSpecial() {
498         assertTrue(Double.isNaN(SparseGradient.hypot(SparseGradient.createVariable(0, Double.NaN),
499                                                                  SparseGradient.createVariable(0, +3.0e250)).getValue()));
500         assertTrue(Double.isNaN(SparseGradient.hypot(SparseGradient.createVariable(0, +3.0e250),
501                                                                  SparseGradient.createVariable(0, Double.NaN)).getValue()));
502         assertTrue(Double.isInfinite(SparseGradient.hypot(SparseGradient.createVariable(0, Double.POSITIVE_INFINITY),
503                                                                       SparseGradient.createVariable(0, +3.0e250)).getValue()));
504         assertTrue(Double.isInfinite(SparseGradient.hypot(SparseGradient.createVariable(0, +3.0e250),
505                                                                       SparseGradient.createVariable(0, Double.POSITIVE_INFINITY)).getValue()));
506     }
507 
508     @Test
509     void testPrimitiveRemainder() {
510         for (double x = -1.7; x < 2; x += 0.2) {
511             SparseGradient sgX = SparseGradient.createVariable(0, x);
512             for (double y = -1.7; y < 2; y += 0.2) {
513                 SparseGradient remainder = sgX.remainder(y);
514                 SparseGradient ref = sgX.subtract(x - FastMath.IEEEremainder(x, y));
515                 SparseGradient zero = remainder.subtract(ref);
516                 checkF0F1(zero, 0.0, 0.0, 0.0);
517             }
518         }
519     }
520 
521     @Test
522     void testRemainder() {
523         for (double x = -1.7; x < 2; x += 0.2) {
524             SparseGradient sgX = SparseGradient.createVariable(0, x);
525             for (double y = -1.7; y < 2; y += 0.2) {
526                 SparseGradient sgY = SparseGradient.createVariable(1, y);
527                 SparseGradient remainder = sgX.remainder(sgY);
528                 SparseGradient ref = sgX.subtract(sgY.multiply((x - FastMath.IEEEremainder(x, y)) / y));
529                 SparseGradient zero = remainder.subtract(ref);
530                 checkF0F1(zero, 0.0, 0.0, 0.0);
531             }
532         }
533     }
534 
535     @Override
536     @Test
537     public void testExp() {
538         for (double x = 0.1; x < 1.2; x += 0.001) {
539             double refExp = FastMath.exp(x);
540             checkF0F1(SparseGradient.createVariable(0, x).exp(), refExp, refExp);
541         }
542     }
543 
544     @Test
545     void testExpm1Definition() {
546         for (double x = 0.1; x < 1.2; x += 0.001) {
547             SparseGradient sgX = SparseGradient.createVariable(0, x);
548             SparseGradient expm11 = sgX.expm1();
549             SparseGradient expm12 = sgX.exp().subtract(sgX.getField().getOne());
550             SparseGradient zero = expm11.subtract(expm12);
551             checkF0F1(zero, 0.0, 0.0);
552         }
553     }
554 
555     @Override
556     @Test
557     public void testLog() {
558         for (double x = 0.1; x < 1.2; x += 0.001) {
559             checkF0F1(SparseGradient.createVariable(0, x).log(), FastMath.log(x), 1.0 / x);
560         }
561     }
562 
563     @Test
564     void testLog1pDefinition() {
565         for (double x = 0.1; x < 1.2; x += 0.001) {
566             SparseGradient sgX = SparseGradient.createVariable(0, x);
567             SparseGradient log1p1 = sgX.log1p();
568             SparseGradient log1p2 = sgX.add(sgX.getField().getOne()).log();
569             SparseGradient zero = log1p1.subtract(log1p2);
570             checkF0F1(zero, 0.0, 0.0);
571         }
572     }
573 
574     @Test
575     void testLog10Definition() {
576         for (double x = 0.1; x < 1.2; x += 0.001) {
577             SparseGradient sgX = SparseGradient.createVariable(0, x);
578             SparseGradient log101 = sgX.log10();
579             SparseGradient log102 = sgX.log().divide(FastMath.log(10.0));
580             SparseGradient zero = log101.subtract(log102);
581             checkF0F1(zero, 0.0, 0.0);
582         }
583     }
584 
585     @Test
586     void testLogExp() {
587         for (double x = 0.1; x < 1.2; x += 0.001) {
588             SparseGradient sgX = SparseGradient.createVariable(0, x);
589             SparseGradient rebuiltX = sgX.exp().log();
590             SparseGradient zero = rebuiltX.subtract(sgX);
591             checkF0F1(zero, 0.0, 0.0);
592         }
593     }
594 
595     @Test
596     void testLog1pExpm1() {
597         for (double x = 0.1; x < 1.2; x += 0.001) {
598             SparseGradient sgX = SparseGradient.createVariable(0, x);
599             SparseGradient rebuiltX = sgX.expm1().log1p();
600             SparseGradient zero = rebuiltX.subtract(sgX);
601             checkF0F1(zero, 0.0, 0.0);
602         }
603     }
604 
605     @Test
606     void testLog10Power() {
607         for (double x = 0.1; x < 1.2; x += 0.001) {
608             SparseGradient sgX = SparseGradient.createVariable(0, x);
609             SparseGradient rebuiltX = SparseGradient.pow(10.0, sgX).log10();
610             SparseGradient zero = rebuiltX.subtract(sgX);
611             checkF0F1(zero, 0.0, 0.0);
612         }
613     }
614 
615     @Test
616     public void testSinCos() {
617         for (double x = 0.1; x < 1.2; x += 0.001) {
618             SparseGradient sgX = SparseGradient.createVariable(0, x);
619             SparseGradient sin = sgX.sin();
620             SparseGradient cos = sgX.cos();
621             FieldSinCos<SparseGradient> sinCos = sgX.sinCos();
622             double s = FastMath.sin(x);
623             double c = FastMath.cos(x);
624             checkF0F1(sin, s, c);
625             checkF0F1(cos, c, -s);
626             checkF0F1(sinCos.sin(), s, c);
627             checkF0F1(sinCos.cos(), c, -s);
628         }
629     }
630 
631     @Test
632     void testSinAsin() {
633         for (double x = 0.1; x < 1.2; x += 0.001) {
634             SparseGradient sgX = SparseGradient.createVariable(0, x);
635             SparseGradient rebuiltX = sgX.sin().asin();
636             SparseGradient zero = rebuiltX.subtract(sgX);
637             checkF0F1(zero, 0.0, 0.0);
638         }
639     }
640 
641     @Test
642     void testCosAcos() {
643         for (double x = 0.1; x < 1.2; x += 0.001) {
644             SparseGradient sgX = SparseGradient.createVariable(0, x);
645             SparseGradient rebuiltX = sgX.cos().acos();
646             SparseGradient zero = rebuiltX.subtract(sgX);
647             checkF0F1(zero, 0.0, 0.0);
648         }
649     }
650 
651     @Test
652     void testTanAtan() {
653         for (double x = 0.1; x < 1.2; x += 0.001) {
654             SparseGradient sgX = SparseGradient.createVariable(0, x);
655             SparseGradient rebuiltX = sgX.tan().atan();
656             SparseGradient zero = rebuiltX.subtract(sgX);
657             checkF0F1(zero, 0.0, 0.0);
658         }
659     }
660 
661     @Test
662     void testTangentDefinition() {
663         for (double x = 0.1; x < 1.2; x += 0.001) {
664             SparseGradient sgX = SparseGradient.createVariable(0, x);
665             SparseGradient tan1 = sgX.sin().divide(sgX.cos());
666             SparseGradient tan2 = sgX.tan();
667             SparseGradient zero = tan1.subtract(tan2);
668             checkF0F1(zero, 0.0, 0.0);
669         }
670     }
671 
672     @Override
673     @Test
674     public void testAtan2() {
675         for (double x = -1.7; x < 2; x += 0.2) {
676             SparseGradient sgX = SparseGradient.createVariable(0, x);
677             for (double y = -1.7; y < 2; y += 0.2) {
678                 SparseGradient sgY = SparseGradient.createVariable(1, y);
679                 SparseGradient atan2 = SparseGradient.atan2(sgY, sgX);
680                 SparseGradient ref = sgY.divide(sgX).atan();
681                 if (x < 0) {
682                     ref = (y < 0) ? ref.subtract(FastMath.PI) : ref.add(FastMath.PI);
683                 }
684                 SparseGradient zero = atan2.subtract(ref);
685                 checkF0F1(zero, 0.0, 0.0);
686             }
687         }
688     }
689 
690     @Test
691     void testAtan2SpecialCasesSparseGradient() {
692 
693         SparseGradient pp =
694                 SparseGradient.atan2(SparseGradient.createVariable(1, +0.0),
695                                           SparseGradient.createVariable(1, +0.0));
696         assertEquals(0, pp.getValue(), 1.0e-15);
697         assertEquals(+1, FastMath.copySign(1, pp.getValue()), 1.0e-15);
698 
699         SparseGradient pn =
700                 SparseGradient.atan2(SparseGradient.createVariable(1, +0.0),
701                                           SparseGradient.createVariable(1, -0.0));
702         assertEquals(FastMath.PI, pn.getValue(), 1.0e-15);
703 
704         SparseGradient np =
705                 SparseGradient.atan2(SparseGradient.createVariable(1, -0.0),
706                                           SparseGradient.createVariable(1, +0.0));
707         assertEquals(0, np.getValue(), 1.0e-15);
708         assertEquals(-1, FastMath.copySign(1, np.getValue()), 1.0e-15);
709 
710         SparseGradient nn =
711                 SparseGradient.atan2(SparseGradient.createVariable(1, -0.0),
712                                           SparseGradient.createVariable(1, -0.0));
713         assertEquals(-FastMath.PI, nn.getValue(), 1.0e-15);
714 
715     }
716 
717     @Test
718     void testSinhDefinition() {
719         for (double x = 0.1; x < 1.2; x += 0.001) {
720             SparseGradient sgX = SparseGradient.createVariable(0, x);
721             SparseGradient sinh1 = sgX.exp().subtract(sgX.exp().reciprocal()).multiply(0.5);
722             SparseGradient sinh2 = sgX.sinh();
723             SparseGradient zero = sinh1.subtract(sinh2);
724             checkF0F1(zero, 0.0, 0.0);
725         }
726     }
727 
728     @Test
729     void testCoshDefinition() {
730         for (double x = 0.1; x < 1.2; x += 0.001) {
731             SparseGradient sgX = SparseGradient.createVariable(0, x);
732             SparseGradient cosh1 = sgX.exp().add(sgX.exp().reciprocal()).multiply(0.5);
733             SparseGradient cosh2 = sgX.cosh();
734             SparseGradient zero = cosh1.subtract(cosh2);
735             checkF0F1(zero, 0.0, 0.0);
736         }
737     }
738 
739     @Test
740     void testTanhDefinition() {
741         for (double x = 0.1; x < 1.2; x += 0.001) {
742             SparseGradient sgX = SparseGradient.createVariable(0, x);
743             SparseGradient tanh1 = sgX.exp().subtract(sgX.exp().reciprocal()).divide(sgX.exp().add(sgX.exp().reciprocal()));
744             SparseGradient tanh2 = sgX.tanh();
745             SparseGradient zero = tanh1.subtract(tanh2);
746             checkF0F1(zero, 0.0, 0.0);
747         }
748     }
749 
750     @Test
751     void testSinhAsinh() {
752         for (double x = 0.1; x < 1.2; x += 0.001) {
753             SparseGradient sgX = SparseGradient.createVariable(0, x);
754             SparseGradient rebuiltX = sgX.sinh().asinh();
755             SparseGradient zero = rebuiltX.subtract(sgX);
756             checkF0F1(zero, 0.0, 0.0);
757         }
758     }
759 
760     @Test
761     void testCoshAcosh() {
762         for (double x = 0.1; x < 1.2; x += 0.001) {
763             SparseGradient sgX = SparseGradient.createVariable(0, x);
764             SparseGradient rebuiltX = sgX.cosh().acosh();
765             SparseGradient zero = rebuiltX.subtract(sgX);
766             checkF0F1(zero, 0.0, 0.0);
767         }
768     }
769 
770     @Test
771     void testTanhAtanh() {
772         for (double x = 0.1; x < 1.2; x += 0.001) {
773             SparseGradient sgX = SparseGradient.createVariable(0, x);
774             SparseGradient rebuiltX = sgX.tanh().atanh();
775             SparseGradient zero = rebuiltX.subtract(sgX);
776             checkF0F1(zero, 0.0, 0.0);
777         }
778     }
779 
780     @Test
781     void testCompositionOneVariableY() {
782         for (double x = 0.1; x < 1.2; x += 0.1) {
783             SparseGradient sgX = SparseGradient.createConstant(x);
784             for (double y = 0.1; y < 1.2; y += 0.1) {
785                 SparseGradient sgY = SparseGradient.createVariable(0, y);
786                 SparseGradient f = sgX.divide(sgY).sqrt();
787                 double f0 = FastMath.sqrt(x / y);
788                 double f1 = -x / (2 * y * y * f0);
789                 checkF0F1(f, f0, f1);
790             }
791         }
792     }
793 
794     @Test
795     void testTaylorPolynomial() {
796         for (double x = 0; x < 1.2; x += 0.1) {
797             SparseGradient sgX = SparseGradient.createVariable(0, x);
798             for (double y = 0; y < 1.2; y += 0.2) {
799                 SparseGradient sgY = SparseGradient.createVariable(1, y);
800                 for (double z = 0; z < 1.2; z += 0.2) {
801                     SparseGradient sgZ = SparseGradient.createVariable(2, z);
802                     SparseGradient f = sgX.multiply(3).add(sgZ.multiply(-2)).add(sgY.multiply(5));
803                     for (double dx = -0.2; dx < 0.2; dx += 0.2) {
804                         for (double dy = -0.2; dy < 0.2; dy += 0.1) {
805                             for (double dz = -0.2; dz < 0.2; dz += 0.1) {
806                                 double ref = 3 * (x + dx) + 5 * (y + dy) -2 * (z + dz);
807                                 assertEquals(ref, f.taylor(dx, dy, dz), 3.0e-15);
808                             }
809                         }
810                     }
811                 }
812             }
813         }
814     }
815 
816     @Test
817     void testTaylorAtan2() {
818         double x0 =  0.1;
819         double y0 = -0.3;
820             SparseGradient sgX   = SparseGradient.createVariable(0, x0);
821             SparseGradient sgY   = SparseGradient.createVariable(1, y0);
822             SparseGradient atan2 = SparseGradient.atan2(sgY, sgX);
823             double maxError = 0;
824             for (double dx = -0.05; dx < 0.05; dx += 0.001) {
825                 for (double dy = -0.05; dy < 0.05; dy += 0.001) {
826                     double ref = FastMath.atan2(y0 + dy, x0 + dx);
827                     maxError = FastMath.max(maxError, FastMath.abs(ref - atan2.taylor(dx, dy)));
828                 }
829             }
830             double expectedError = 0.0241;
831             assertEquals(expectedError, maxError, 0.01 * expectedError);
832     }
833 
834     @Test
835     public void testAbs() {
836 
837         SparseGradient minusOne = SparseGradient.createVariable(0, -1.0);
838         assertEquals(+1.0, minusOne.abs().getValue(), 1.0e-15);
839         assertEquals(-1.0, minusOne.abs().getDerivative(0), 1.0e-15);
840 
841         SparseGradient plusOne = SparseGradient.createVariable(0, +1.0);
842         assertEquals(+1.0, plusOne.abs().getValue(), 1.0e-15);
843         assertEquals(+1.0, plusOne.abs().getDerivative(0), 1.0e-15);
844 
845         SparseGradient minusZero = SparseGradient.createVariable(0, -0.0);
846         assertEquals(+0.0, minusZero.abs().getValue(), 1.0e-15);
847         assertEquals(-1.0, minusZero.abs().getDerivative(0), 1.0e-15);
848 
849         SparseGradient plusZero = SparseGradient.createVariable(0, +0.0);
850         assertEquals(+0.0, plusZero.abs().getValue(), 1.0e-15);
851         assertEquals(+1.0, plusZero.abs().getDerivative(0), 1.0e-15);
852 
853     }
854 
855     @Override
856     @Test
857     public void testSign() {
858 
859         SparseGradient minusOne = SparseGradient.createVariable(0, -1.0);
860         assertEquals(-1.0, minusOne.sign().getValue(), 1.0e-15);
861         assertEquals( 0.0, minusOne.sign().getDerivative(0), 1.0e-15);
862 
863         SparseGradient plusOne = SparseGradient.createVariable(0, +1.0);
864         assertEquals(+1.0, plusOne.sign().getValue(), 1.0e-15);
865         assertEquals( 0.0, plusOne.sign().getDerivative(0), 1.0e-15);
866 
867         SparseGradient minusZero = SparseGradient.createVariable(0, -0.0);
868         assertEquals(-0.0, minusZero.sign().getValue(), 1.0e-15);
869         assertTrue(Double.doubleToLongBits(minusZero.sign().getValue()) < 0);
870         assertEquals( 0.0, minusZero.sign().getDerivative(0), 1.0e-15);
871 
872         SparseGradient plusZero = SparseGradient.createVariable(0, +0.0);
873         assertEquals(+0.0, plusZero.sign().getValue(), 1.0e-15);
874         assertEquals(0, Double.doubleToLongBits(plusZero.sign().getValue()));
875         assertEquals( 0.0, plusZero.sign().getDerivative(0), 1.0e-15);
876 
877     }
878 
879     @Test
880     void testCeilFloorRintLong() {
881 
882         SparseGradient x = SparseGradient.createVariable(0, -1.5);
883         assertEquals(-1.5, x.getValue(), 1.0e-15);
884         assertEquals(+1.0, x.getDerivative(0), 1.0e-15);
885         assertEquals(-1.0, x.ceil().getValue(), 1.0e-15);
886         assertEquals(+0.0, x.ceil().getDerivative(0), 1.0e-15);
887         assertEquals(-2.0, x.floor().getValue(), 1.0e-15);
888         assertEquals(+0.0, x.floor().getDerivative(0), 1.0e-15);
889         assertEquals(-2.0, x.rint().getValue(), 1.0e-15);
890         assertEquals(+0.0, x.rint().getDerivative(0), 1.0e-15);
891         assertEquals(-2.0, x.subtract(x.getField().getOne()).rint().getValue(), 1.0e-15);
892 
893     }
894 
895     @Test
896     void testCopySign() {
897 
898         SparseGradient minusOne = SparseGradient.createVariable(0, -1.0);
899         assertEquals(+1.0, minusOne.copySign(+1.0).getValue(), 1.0e-15);
900         assertEquals(-1.0, minusOne.copySign(+1.0).getDerivative(0), 1.0e-15);
901         assertEquals(-1.0, minusOne.copySign(-1.0).getValue(), 1.0e-15);
902         assertEquals(+1.0, minusOne.copySign(-1.0).getDerivative(0), 1.0e-15);
903         assertEquals(+1.0, minusOne.copySign(+0.0).getValue(), 1.0e-15);
904         assertEquals(-1.0, minusOne.copySign(+0.0).getDerivative(0), 1.0e-15);
905         assertEquals(-1.0, minusOne.copySign(-0.0).getValue(), 1.0e-15);
906         assertEquals(+1.0, minusOne.copySign(-0.0).getDerivative(0), 1.0e-15);
907         assertEquals(+1.0, minusOne.copySign(Double.NaN).getValue(), 1.0e-15);
908         assertEquals(-1.0, minusOne.copySign(Double.NaN).getDerivative(0), 1.0e-15);
909 
910         SparseGradient plusOne = SparseGradient.createVariable(0, +1.0);
911         assertEquals(+1.0, plusOne.copySign(+1.0).getValue(), 1.0e-15);
912         assertEquals(+1.0, plusOne.copySign(+1.0).getDerivative(0), 1.0e-15);
913         assertEquals(-1.0, plusOne.copySign(-1.0).getValue(), 1.0e-15);
914         assertEquals(-1.0, plusOne.copySign(-1.0).getDerivative(0), 1.0e-15);
915         assertEquals(+1.0, plusOne.copySign(+0.0).getValue(), 1.0e-15);
916         assertEquals(+1.0, plusOne.copySign(+0.0).getDerivative(0), 1.0e-15);
917         assertEquals(-1.0, plusOne.copySign(-0.0).getValue(), 1.0e-15);
918         assertEquals(-1.0, plusOne.copySign(-0.0).getDerivative(0), 1.0e-15);
919         assertEquals(+1.0, plusOne.copySign(Double.NaN).getValue(), 1.0e-15);
920         assertEquals(+1.0, plusOne.copySign(Double.NaN).getDerivative(0), 1.0e-15);
921 
922     }
923 
924     @Test
925     void testToDegreesDefinition() {
926         double epsilon = 3.0e-16;
927         for (int maxOrder = 0; maxOrder < 6; ++maxOrder) {
928             for (double x = 0.1; x < 1.2; x += 0.001) {
929                 SparseGradient sgX = SparseGradient.createVariable(0, x);
930                 assertEquals(FastMath.toDegrees(x), sgX.toDegrees().getValue(), epsilon);
931                 assertEquals(180 / FastMath.PI, sgX.toDegrees().getDerivative(0), epsilon);
932             }
933         }
934     }
935 
936     @Test
937     void testToRadiansDefinition() {
938         double epsilon = 3.0e-16;
939         for (int maxOrder = 0; maxOrder < 6; ++maxOrder) {
940             for (double x = 0.1; x < 1.2; x += 0.001) {
941                 SparseGradient sgX = SparseGradient.createVariable(0, x);
942                 assertEquals(FastMath.toRadians(x), sgX.toRadians().getValue(), epsilon);
943                 assertEquals(FastMath.PI / 180, sgX.toRadians().getDerivative(0), epsilon);
944             }
945         }
946     }
947 
948     @Test
949     void testDegRad() {
950         for (double x = 0.1; x < 1.2; x += 0.001) {
951             SparseGradient sgX = SparseGradient.createVariable(0, x);
952             SparseGradient rebuiltX = sgX.toDegrees().toRadians();
953             SparseGradient zero = rebuiltX.subtract(sgX);
954             checkF0F1(zero, 0, 0);
955         }
956     }
957 
958     @Test
959     void testCompose() {
960         PolynomialFunction poly =
961                 new PolynomialFunction(new double[] { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 });
962         for (double x = 0.1; x < 1.2; x += 0.001) {
963             SparseGradient sgX = SparseGradient.createVariable(0, x);
964             SparseGradient sgY1 = sgX.getField().getZero();
965             for (int i = poly.degree(); i >= 0; --i) {
966                 sgY1 = sgY1.multiply(sgX).add(poly.getCoefficients()[i]);
967             }
968             SparseGradient sgY2 = sgX.compose(poly.value(x), poly.polynomialDerivative().value(x));
969             SparseGradient zero = sgY1.subtract(sgY2);
970             checkF0F1(zero, 0.0, 0.0);
971         }
972     }
973 
974     @Test
975     void testField() {
976             SparseGradient x = SparseGradient.createVariable(0, 1.0);
977             checkF0F1(x.getField().getZero(), 0.0, 0.0, 0.0, 0.0);
978             checkF0F1(x.getField().getOne(), 1.0, 0.0, 0.0, 0.0);
979             assertEquals(SparseGradient.class, x.getField().getRuntimeClass());
980     }
981 
982     @Test
983     void testLinearCombination1DSDS() {
984         final SparseGradient[] a = new SparseGradient[] {
985             SparseGradient.createVariable(0, -1321008684645961.0 / 268435456.0),
986             SparseGradient.createVariable(1, -5774608829631843.0 / 268435456.0),
987             SparseGradient.createVariable(2, -7645843051051357.0 / 8589934592.0)
988         };
989         final SparseGradient[] b = new SparseGradient[] {
990             SparseGradient.createVariable(3, -5712344449280879.0 / 2097152.0),
991             SparseGradient.createVariable(4, -4550117129121957.0 / 2097152.0),
992             SparseGradient.createVariable(5, 8846951984510141.0 / 131072.0)
993         };
994 
995         final SparseGradient abSumInline = a[0].linearCombination(a[0], b[0], a[1], b[1], a[2], b[2]);
996         final SparseGradient abSumArray = a[0].linearCombination(a, b);
997 
998         assertEquals(abSumInline.getValue(), abSumArray.getValue(), 1.0e-15);
999         assertEquals(-1.8551294182586248737720779899, abSumInline.getValue(), 1.0e-15);
1000         assertEquals(b[0].getValue(), abSumInline.getDerivative(0), 1.0e-15);
1001         assertEquals(b[1].getValue(), abSumInline.getDerivative(1), 1.0e-15);
1002         assertEquals(b[2].getValue(), abSumInline.getDerivative(2), 1.0e-15);
1003         assertEquals(a[0].getValue(), abSumInline.getDerivative(3), 1.0e-15);
1004         assertEquals(a[1].getValue(), abSumInline.getDerivative(4), 1.0e-15);
1005         assertEquals(a[2].getValue(), abSumInline.getDerivative(5), 1.0e-15);
1006 
1007     }
1008 
1009     @Test
1010     void testLinearCombination1DoubleDS() {
1011         final double[] a = new double[] {
1012             -1321008684645961.0 / 268435456.0,
1013             -5774608829631843.0 / 268435456.0,
1014             -7645843051051357.0 / 8589934592.0
1015         };
1016         final SparseGradient[] b = new SparseGradient[] {
1017             SparseGradient.createVariable(0, -5712344449280879.0 / 2097152.0),
1018             SparseGradient.createVariable(1, -4550117129121957.0 / 2097152.0),
1019             SparseGradient.createVariable(2, 8846951984510141.0 / 131072.0)
1020         };
1021 
1022         final SparseGradient abSumInline = b[0].linearCombination(a[0], b[0],
1023                                                                        a[1], b[1],
1024                                                                        a[2], b[2]);
1025         final SparseGradient abSumArray = b[0].linearCombination(a, b);
1026 
1027         assertEquals(abSumInline.getValue(), abSumArray.getValue(), 1.0e-15);
1028         assertEquals(-1.8551294182586248737720779899, abSumInline.getValue(), 1.0e-15);
1029         assertEquals(a[0], abSumInline.getDerivative(0), 1.0e-15);
1030         assertEquals(a[1], abSumInline.getDerivative(1), 1.0e-15);
1031         assertEquals(a[2], abSumInline.getDerivative(2), 1.0e-15);
1032 
1033     }
1034 
1035     @Test
1036     void testLinearCombination2DSDS() {
1037         // we compare accurate versus naive dot product implementations
1038         // on regular vectors (i.e. not extreme cases like in the previous test)
1039         Well1024a random = new Well1024a(0xc6af886975069f11l);
1040 
1041         for (int i = 0; i < 10000; ++i) {
1042             final SparseGradient[] u = new SparseGradient[4];
1043             final SparseGradient[] v = new SparseGradient[4];
1044             for (int j = 0; j < u.length; ++j) {
1045                 u[j] = SparseGradient.createVariable(j, 1e17 * random.nextDouble());
1046                 v[j] = SparseGradient.createConstant(1e17 * random.nextDouble());
1047             }
1048 
1049             SparseGradient lin = u[0].linearCombination(u[0], v[0], u[1], v[1]);
1050             double ref = u[0].getValue() * v[0].getValue() +
1051                          u[1].getValue() * v[1].getValue();
1052             assertEquals(ref, lin.getValue(), 1.0e-15 * FastMath.abs(ref));
1053             assertEquals(v[0].getValue(), lin.getDerivative(0), 1.0e-15 * FastMath.abs(v[0].getValue()));
1054             assertEquals(v[1].getValue(), lin.getDerivative(1), 1.0e-15 * FastMath.abs(v[1].getValue()));
1055 
1056             lin = u[0].linearCombination(u[0], v[0], u[1], v[1], u[2], v[2]);
1057             ref = u[0].getValue() * v[0].getValue() +
1058                   u[1].getValue() * v[1].getValue() +
1059                   u[2].getValue() * v[2].getValue();
1060             assertEquals(ref, lin.getValue(), 1.0e-15 * FastMath.abs(ref));
1061             assertEquals(v[0].getValue(), lin.getDerivative(0), 1.0e-15 * FastMath.abs(v[0].getValue()));
1062             assertEquals(v[1].getValue(), lin.getDerivative(1), 1.0e-15 * FastMath.abs(v[1].getValue()));
1063             assertEquals(v[2].getValue(), lin.getDerivative(2), 1.0e-15 * FastMath.abs(v[2].getValue()));
1064 
1065             lin = u[0].linearCombination(u[0], v[0], u[1], v[1], u[2], v[2], u[3], v[3]);
1066             ref = u[0].getValue() * v[0].getValue() +
1067                   u[1].getValue() * v[1].getValue() +
1068                   u[2].getValue() * v[2].getValue() +
1069                   u[3].getValue() * v[3].getValue();
1070             assertEquals(ref, lin.getValue(), 1.0e-15 * FastMath.abs(ref));
1071             assertEquals(v[0].getValue(), lin.getDerivative(0), 1.0e-15 * FastMath.abs(v[0].getValue()));
1072             assertEquals(v[1].getValue(), lin.getDerivative(1), 1.0e-15 * FastMath.abs(v[1].getValue()));
1073             assertEquals(v[2].getValue(), lin.getDerivative(2), 1.0e-15 * FastMath.abs(v[2].getValue()));
1074             assertEquals(v[3].getValue(), lin.getDerivative(3), 1.0e-15 * FastMath.abs(v[3].getValue()));
1075 
1076         }
1077     }
1078 
1079     @Test
1080     void testLinearCombination2DoubleDS() {
1081         // we compare accurate versus naive dot product implementations
1082         // on regular vectors (i.e. not extreme cases like in the previous test)
1083         Well1024a random = new Well1024a(0xc6af886975069f11l);
1084 
1085         for (int i = 0; i < 10000; ++i) {
1086             final double[] u = new double[4];
1087             final SparseGradient[] v = new SparseGradient[4];
1088             for (int j = 0; j < u.length; ++j) {
1089                 u[j] = 1e17 * random.nextDouble();
1090                 v[j] = SparseGradient.createVariable(j, 1e17 * random.nextDouble());
1091             }
1092 
1093             SparseGradient lin = v[0].linearCombination(u[0], v[0], u[1], v[1]);
1094             double ref = u[0] * v[0].getValue() +
1095                          u[1] * v[1].getValue();
1096             assertEquals(ref, lin.getValue(), 1.0e-15 * FastMath.abs(ref));
1097             assertEquals(u[0], lin.getDerivative(0), 1.0e-15 * FastMath.abs(v[0].getValue()));
1098             assertEquals(u[1], lin.getDerivative(1), 1.0e-15 * FastMath.abs(v[1].getValue()));
1099 
1100             lin = v[0].linearCombination(u[0], v[0], u[1], v[1], u[2], v[2]);
1101             ref = u[0] * v[0].getValue() +
1102                   u[1] * v[1].getValue() +
1103                   u[2] * v[2].getValue();
1104             assertEquals(ref, lin.getValue(), 1.0e-15 * FastMath.abs(ref));
1105             assertEquals(u[0], lin.getDerivative(0), 1.0e-15 * FastMath.abs(v[0].getValue()));
1106             assertEquals(u[1], lin.getDerivative(1), 1.0e-15 * FastMath.abs(v[1].getValue()));
1107             assertEquals(u[2], lin.getDerivative(2), 1.0e-15 * FastMath.abs(v[2].getValue()));
1108 
1109             lin = v[0].linearCombination(u[0], v[0], u[1], v[1], u[2], v[2], u[3], v[3]);
1110             ref = u[0] * v[0].getValue() +
1111                   u[1] * v[1].getValue() +
1112                   u[2] * v[2].getValue() +
1113                   u[3] * v[3].getValue();
1114             assertEquals(ref, lin.getValue(), 1.0e-15 * FastMath.abs(ref));
1115             assertEquals(u[0], lin.getDerivative(0), 1.0e-15 * FastMath.abs(v[0].getValue()));
1116             assertEquals(u[1], lin.getDerivative(1), 1.0e-15 * FastMath.abs(v[1].getValue()));
1117             assertEquals(u[2], lin.getDerivative(2), 1.0e-15 * FastMath.abs(v[2].getValue()));
1118             assertEquals(u[3], lin.getDerivative(3), 1.0e-15 * FastMath.abs(v[3].getValue()));
1119 
1120         }
1121     }
1122 
1123     @Test
1124     void testSerialization() {
1125         SparseGradient a = SparseGradient.createVariable(0, 1.3);
1126         SparseGradient b = (SparseGradient) UnitTestUtils.serializeAndRecover(a);
1127         assertEquals(a, b);
1128     }
1129 
1130     @Test
1131     void testZero() {
1132         SparseGradient zero = SparseGradient.createVariable(0, 17.0).getField().getZero();
1133         assertEquals(0, zero.getFreeParameters());
1134         assertEquals(0.0, zero.getValue(), 1.0e-15);
1135         assertEquals(0.0, zero.getDerivative(0), 1.0e-15);
1136     }
1137 
1138     @Test
1139     void testOne() {
1140         SparseGradient one = SparseGradient.createVariable(0, 17.0).getField().getOne();
1141         assertEquals(0, one.getFreeParameters());
1142         assertEquals(1.0, one.getValue(), 1.0e-15);
1143         assertEquals(0.0, one.getDerivative(0), 1.0e-15);
1144     }
1145 
1146     @Test
1147     void testMap() {
1148         List<int[]> pairs = new ArrayList<>();
1149         for (int parameters = 1; parameters < 5; ++parameters) {
1150             for (int order = 0; order < 3; ++order) {
1151                 pairs.add(new int[] { parameters, order });
1152             }
1153         }
1154         Map<Field<?>, Integer> map = new HashMap<>();
1155         for (int i = 0; i < 1000; ++i) {
1156             map.put(SparseGradient.createVariable(i, 17.0).getField(), 0);
1157         }
1158 
1159         assertEquals(1, map.size());
1160         @SuppressWarnings("unchecked")
1161         Field<SparseGradient> first = (Field<SparseGradient>) map.entrySet().iterator().next().getKey();
1162         assertEquals(first, first);
1163         assertNotEquals(first, new DSFactory(1, 1).constant(0.0).getField());
1164 
1165     }
1166 
1167     @Test
1168     void testRunTimeClass() {
1169         Field<SparseGradient> field = SparseGradient.createVariable(5, 17.0).getField();
1170         assertEquals(SparseGradient.class, field.getRuntimeClass());
1171     }
1172 
1173     private void checkF0F1(SparseGradient sg, double value, double...derivatives) {
1174 
1175         // check value
1176         assertEquals(value, sg.getValue(), 1.0e-13);
1177 
1178         // check first order derivatives
1179         for (int i = 0; i < derivatives.length; ++i) {
1180             assertEquals(derivatives[i], sg.getDerivative(i), 1.0e-13);
1181         }
1182 
1183     }
1184 
1185 }