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