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.io.Serializable;
25  import java.util.Collections;
26  import java.util.HashMap;
27  import java.util.Map;
28  
29  import org.hipparchus.Field;
30  import org.hipparchus.exception.MathIllegalArgumentException;
31  import org.hipparchus.util.FastMath;
32  import org.hipparchus.util.MathArrays;
33  import org.hipparchus.util.MathUtils;
34  import org.hipparchus.util.Precision;
35  
36  /**
37   * First derivative computation with large number of variables.
38   * <p>
39   * This class plays a similar role to {@link DerivativeStructure}, with
40   * a focus on efficiency when dealing with large number of independent variables
41   * and most computation depend only on a few of them, and when only first derivative
42   * is desired. When these conditions are met, this class should be much faster than
43   * {@link DerivativeStructure} and use less memory.
44   * </p>
45   *
46   */
47  public class SparseGradient implements Derivative1<SparseGradient>, Serializable {
48  
49      /** Serializable UID. */
50      private static final long serialVersionUID = 20131025L;
51  
52      /** Value of the calculation. */
53      private double value;
54  
55      /** Stored derivative, each key representing a different independent variable. */
56      private final Map<Integer, Double> derivatives;
57  
58      /** Internal constructor.
59       * @param value value of the function
60       * @param derivatives derivatives map, a deep copy will be performed,
61       * so the map given here will remain safe from changes in the new instance,
62       * may be null to create an empty derivatives map, i.e. a constant value
63       */
64      private SparseGradient(final double value, final Map<Integer, Double> derivatives) {
65          this.value = value;
66          this.derivatives = new HashMap<>();
67          if (derivatives != null) {
68              this.derivatives.putAll(derivatives);
69          }
70      }
71  
72      /** Internal constructor.
73       * @param value value of the function
74       * @param scale scaling factor to apply to all derivatives
75       * @param derivatives derivatives map, a deep copy will be performed,
76       * so the map given here will remain safe from changes in the new instance,
77       * may be null to create an empty derivatives map, i.e. a constant value
78       */
79      private SparseGradient(final double value, final double scale,
80                               final Map<Integer, Double> derivatives) {
81          this.value = value;
82          this.derivatives = new HashMap<>();
83          if (derivatives != null) {
84              for (final Map.Entry<Integer, Double> entry : derivatives.entrySet()) {
85                  this.derivatives.put(entry.getKey(), scale * entry.getValue());
86              }
87          }
88      }
89  
90      /** {@inheritDoc} */
91      @Override
92      public int getFreeParameters() {
93          return derivatives.size();
94      }
95  
96      /** {@inheritDoc} */
97      @Override
98      public double getPartialDerivative(int... orders) throws MathIllegalArgumentException {
99          return getDerivative(orders[0]);
100     }
101 
102     /** {@inheritDoc} */
103     @Override
104     public SparseGradient newInstance(final double v) {
105         return createConstant(v);
106     }
107 
108     /** {@inheritDoc} */
109     @Override
110     public SparseGradient withValue(final double v) {
111         return new SparseGradient(v, derivatives);
112     }
113 
114     /** Factory method creating a constant.
115      * @param value value of the constant
116      * @return a new instance
117      */
118     public static SparseGradient createConstant(final double value) {
119         return new SparseGradient(value, Collections.emptyMap());
120     }
121 
122     /** Factory method creating an independent variable.
123      * @param idx index of the variable
124      * @param value value of the variable
125      * @return a new instance
126      */
127     public static SparseGradient createVariable(final int idx, final double value) {
128         return new SparseGradient(value, Collections.singletonMap(idx, 1.0));
129     }
130 
131     /**
132      * Get the derivative with respect to a particular index variable.
133      *
134      * @param index index to differentiate with.
135      * @return derivative with respect to a particular index variable
136      */
137     public double getDerivative(final int index) {
138         final Double out = derivatives.get(index);
139         return (out == null) ? 0.0 : out;
140     }
141 
142     /** {@inheritDoc} */
143     @Override
144     public SparseGradient getAddendum() {
145         return new SparseGradient(0, derivatives);
146     }
147 
148     /**
149      * Get the value of the function.
150      * @return value of the function.
151      */
152     @Override
153     public double getValue() {
154         return value;
155     }
156 
157     /** {@inheritDoc} */
158     @Override
159     public SparseGradient add(final SparseGradient a) {
160         final SparseGradient out = new SparseGradient(value + a.value, derivatives);
161         for (Map.Entry<Integer, Double> entry : a.derivatives.entrySet()) {
162             final int id = entry.getKey();
163             out.derivatives.merge(id, entry.getValue(), Double::sum);
164         }
165 
166         return out;
167     }
168 
169     /**
170      * Add in place.
171      * <p>
172      * This method is designed to be faster when used multiple times in a loop.
173      * </p>
174      * <p>
175      * The instance is changed here, in order to not change the
176      * instance the {@link #add(SparseGradient)} method should
177      * be used.
178      * </p>
179      * @param a instance to add
180      */
181     public void addInPlace(final SparseGradient a) {
182         value += a.value;
183         for (final Map.Entry<Integer, Double> entry : a.derivatives.entrySet()) {
184             final int id = entry.getKey();
185             derivatives.merge(id, entry.getValue(), Double::sum);
186         }
187     }
188 
189     /** {@inheritDoc} */
190     @Override
191     public SparseGradient subtract(final SparseGradient a) {
192         final SparseGradient out = new SparseGradient(value - a.value, derivatives);
193         for (Map.Entry<Integer, Double> entry : a.derivatives.entrySet()) {
194             final int id = entry.getKey();
195             final Double old = out.derivatives.get(id);
196             if (old == null) {
197                 out.derivatives.put(id, -entry.getValue());
198             } else {
199                 out.derivatives.put(id, old - entry.getValue());
200             }
201         }
202         return out;
203     }
204 
205     /** {@inheritDoc} */
206     @Override
207     public SparseGradient multiply(final SparseGradient a) {
208         final SparseGradient out =
209             new SparseGradient(value * a.value, Collections.emptyMap());
210 
211         // Derivatives.
212         for (Map.Entry<Integer, Double> entry : derivatives.entrySet()) {
213             out.derivatives.put(entry.getKey(), a.value * entry.getValue());
214         }
215         for (Map.Entry<Integer, Double> entry : a.derivatives.entrySet()) {
216             final int id = entry.getKey();
217             out.derivatives.merge(id, value * entry.getValue(), Double::sum);
218         }
219         return out;
220     }
221 
222     /**
223      * Multiply in place.
224      * <p>
225      * This method is designed to be faster when used multiple times in a loop.
226      * </p>
227      * <p>
228      * The instance is changed here, in order to not change the
229      * instance the {@link #add(SparseGradient)} method should
230      * be used.
231      * </p>
232      * @param a instance to multiply
233      */
234     public void multiplyInPlace(final SparseGradient a) {
235         // Derivatives.
236         derivatives.replaceAll((k, v) -> a.value * v);
237         for (Map.Entry<Integer, Double> entry : a.derivatives.entrySet()) {
238             final int id = entry.getKey();
239             derivatives.merge(id, value * entry.getValue(), Double::sum);
240         }
241         value *= a.value;
242     }
243 
244     /** {@inheritDoc} */
245     @Override
246     public SparseGradient multiply(final double c) {
247         return new SparseGradient(value * c, c, derivatives);
248     }
249 
250     /** {@inheritDoc} */
251     @Override
252     public SparseGradient multiply(final int n) {
253         return new SparseGradient(value * n, n, derivatives);
254     }
255 
256     /** {@inheritDoc} */
257     @Override
258     public SparseGradient divide(final SparseGradient a) {
259         final SparseGradient out = new SparseGradient(value / a.value, Collections.emptyMap());
260 
261         // Derivatives.
262         for (Map.Entry<Integer, Double> entry : derivatives.entrySet()) {
263             out.derivatives.put(entry.getKey(), entry.getValue() / a.value);
264         }
265         for (Map.Entry<Integer, Double> entry : a.derivatives.entrySet()) {
266             final int id = entry.getKey();
267             final Double old = out.derivatives.get(id);
268             if (old == null) {
269                 out.derivatives.put(id, -out.value / a.value * entry.getValue());
270             } else {
271                 out.derivatives.put(id, old - out.value / a.value * entry.getValue());
272             }
273         }
274         return out;
275     }
276 
277     /** {@inheritDoc} */
278     @Override
279     public SparseGradient divide(final double c) {
280         return new SparseGradient(value / c, 1.0 / c, derivatives);
281     }
282 
283     /** {@inheritDoc} */
284     @Override
285     public SparseGradient negate() {
286         return new SparseGradient(-value, -1.0, derivatives);
287     }
288 
289     /** {@inheritDoc} */
290     @Override
291     public Field<SparseGradient> getField() {
292         return SparseGradientField.getInstance();
293     }
294 
295     /** Local field for sparse gradient. */
296     private static class SparseGradientField implements Field<SparseGradient> {
297 
298         /** Zero constant. */
299         private final SparseGradient zero;
300 
301         /** One constant. */
302         private final SparseGradient one;
303 
304         /** Private constructor for the singleton.
305          */
306         private SparseGradientField() {
307             zero = createConstant(0);
308             one  = createConstant(1);
309         }
310 
311         /** Get the unique instance.
312          * @return the unique instance
313          */
314         public static SparseGradientField getInstance() {
315             return LazyHolder.INSTANCE;
316         }
317 
318         /** {@inheritDoc} */
319         @Override
320         public SparseGradient getZero() {
321             return zero;
322         }
323 
324         /** {@inheritDoc} */
325         @Override
326         public SparseGradient getOne() {
327             return one;
328         }
329 
330         /** {@inheritDoc} */
331         @Override
332         public Class<SparseGradient> getRuntimeClass() {
333             return SparseGradient.class;
334         }
335 
336         /** {@inheritDoc} */
337         @Override
338         public boolean equals(final Object other) {
339             return this == other;
340         }
341 
342         /** {@inheritDoc} */
343         @Override
344         public int hashCode() {
345             return 0x142aeff7;
346         }
347 
348         // CHECKSTYLE: stop HideUtilityClassConstructor
349         /** Holder for the instance.
350          * <p>We use here the Initialization On Demand Holder Idiom.</p>
351          */
352         private static class LazyHolder {
353             /** Cached field instance. */
354             private static final SparseGradientField INSTANCE = new SparseGradientField();
355         }
356         // CHECKSTYLE: resume HideUtilityClassConstructor
357 
358     }
359 
360     /** {@inheritDoc} */
361     @Override
362     public SparseGradient remainder(final double a) {
363         return new SparseGradient(FastMath.IEEEremainder(value, a), derivatives);
364     }
365 
366     /** {@inheritDoc} */
367     @Override
368     public SparseGradient remainder(final SparseGradient a) {
369 
370         // compute k such that lhs % rhs = lhs - k rhs
371         final double rem = FastMath.IEEEremainder(value, a.value);
372         final double k   = FastMath.rint((value - rem) / a.value);
373 
374         return subtract(a.multiply(k));
375 
376     }
377 
378     /** {@inheritDoc} */
379     @Override
380     public SparseGradient abs() {
381         if (Double.doubleToLongBits(value) < 0) {
382             // we use the bits representation to also handle -0.0
383             return negate();
384         } else {
385             return this;
386         }
387     }
388 
389     /** {@inheritDoc} */
390     @Override
391     public SparseGradient copySign(final SparseGradient sign) {
392         final long m = Double.doubleToLongBits(value);
393         final long s = Double.doubleToLongBits(sign.value);
394         if ((m >= 0 && s >= 0) || (m < 0 && s < 0)) { // Sign is currently OK
395             return this;
396         }
397         return negate(); // flip sign
398     }
399 
400     /** {@inheritDoc} */
401     @Override
402     public SparseGradient copySign(final double sign) {
403         final long m = Double.doubleToLongBits(value);
404         final long s = Double.doubleToLongBits(sign);
405         if ((m >= 0 && s >= 0) || (m < 0 && s < 0)) { // Sign is currently OK
406             return this;
407         }
408         return negate(); // flip sign
409     }
410 
411     /** {@inheritDoc} */
412     @Override
413     public SparseGradient scalb(final int n) {
414         final SparseGradient out = new SparseGradient(FastMath.scalb(value, n), Collections.emptyMap());
415         for (Map.Entry<Integer, Double> entry : derivatives.entrySet()) {
416             out.derivatives.put(entry.getKey(), FastMath.scalb(entry.getValue(), n));
417         }
418         return out;
419     }
420 
421     /** {@inheritDoc} */
422     @Override
423     public SparseGradient hypot(final SparseGradient y) {
424         if (Double.isInfinite(value) || Double.isInfinite(y.value)) {
425             return createConstant(Double.POSITIVE_INFINITY);
426         } else if (Double.isNaN(value) || Double.isNaN(y.value)) {
427             return createConstant(Double.NaN);
428         } else {
429 
430             final int expX = FastMath.getExponent(value);
431             final int expY = FastMath.getExponent(y.value);
432             if (expX > expY + 27) {
433                 // y is negligible with respect to x
434                 return abs();
435             } else if (expY > expX + 27) {
436                 // x is negligible with respect to y
437                 return y.abs();
438             } else {
439 
440                 // find an intermediate scale to avoid both overflow and underflow
441                 final int middleExp = (expX + expY) / 2;
442 
443                 // scale parameters without losing precision
444                 final SparseGradient scaledX = scalb(-middleExp);
445                 final SparseGradient scaledY = y.scalb(-middleExp);
446 
447                 // compute scaled hypotenuse
448                 final SparseGradient scaledH =
449                         scaledX.multiply(scaledX).add(scaledY.multiply(scaledY)).sqrt();
450 
451                 // remove scaling
452                 return scaledH.scalb(middleExp);
453 
454             }
455 
456         }
457     }
458 
459     /**
460      * Returns the hypotenuse of a triangle with sides {@code x} and {@code y}
461      * - sqrt(<i>x</i><sup>2</sup>&nbsp;+<i>y</i><sup>2</sup>)
462      * avoiding intermediate overflow or underflow.
463      *
464      * <ul>
465      * <li> If either argument is infinite, then the result is positive infinity.</li>
466      * <li> else, if either argument is NaN then the result is NaN.</li>
467      * </ul>
468      *
469      * @param x a value
470      * @param y a value
471      * @return sqrt(<i>x</i><sup>2</sup>&nbsp;+<i>y</i><sup>2</sup>)
472      */
473     public static SparseGradient hypot(final SparseGradient x, final SparseGradient y) {
474         return x.hypot(y);
475     }
476 
477     /** {@inheritDoc} */
478     @Override
479     public SparseGradient sqrt() {
480         final double sqrt = FastMath.sqrt(value);
481         return new SparseGradient(sqrt, 0.5 / sqrt, derivatives);
482     }
483 
484     /** {@inheritDoc} */
485     @Override
486     public SparseGradient pow(final double p) {
487         return new SparseGradient(FastMath.pow(value,  p), p * FastMath.pow(value,  p - 1), derivatives);
488     }
489 
490     /** {@inheritDoc} */
491     @Override
492     public SparseGradient pow(final int n) {
493         if (n == 0) {
494             return getField().getOne();
495         } else {
496             final double valueNm1 = FastMath.pow(value,  n - 1);
497             return new SparseGradient(value * valueNm1, n * valueNm1, derivatives);
498         }
499     }
500 
501     /** Compute a<sup>x</sup> where a is a double and x a {@link SparseGradient}
502      * @param a number to exponentiate
503      * @param x power to apply
504      * @return a<sup>x</sup>
505      */
506     public static SparseGradient pow(final double a, final SparseGradient x) {
507         if (a == 0) {
508             if (x.value == 0) {
509                 return x.compose(1.0, Double.NEGATIVE_INFINITY);
510             } else if (x.value < 0) {
511                 return x.compose(Double.NaN, Double.NaN);
512             } else {
513                 return x.getField().getZero();
514             }
515         } else {
516             final double ax = FastMath.pow(a, x.value);
517             return new SparseGradient(ax, ax * FastMath.log(a), x.derivatives);
518         }
519     }
520 
521     /** {@inheritDoc} */
522     @Override
523     public SparseGradient atan2(final SparseGradient x) {
524 
525         // compute r = sqrt(x^2+y^2)
526         final SparseGradient r = square().add(x.square()).sqrt();
527 
528         final SparseGradient a;
529         if (x.value >= 0) {
530 
531             // compute atan2(y, x) = 2 atan(y / (r + x))
532             a = divide(r.add(x)).atan().multiply(2);
533 
534         } else {
535 
536             // compute atan2(y, x) = +/- pi - 2 atan(y / (r - x))
537             final SparseGradient tmp = divide(r.subtract(x)).atan().multiply(-2);
538             a = tmp.add(tmp.value <= 0 ? -FastMath.PI : FastMath.PI);
539 
540         }
541 
542         // fix value to take special cases (+0/+0, +0/-0, -0/+0, -0/-0, +/-infinity) correctly
543         a.value = FastMath.atan2(value, x.value);
544 
545         return a;
546 
547     }
548 
549     /** Two arguments arc tangent operation.
550      * @param y first argument of the arc tangent
551      * @param x second argument of the arc tangent
552      * @return atan2(y, x)
553      */
554     public static SparseGradient atan2(final SparseGradient y, final SparseGradient x) {
555         return y.atan2(x);
556     }
557 
558     /** {@inheritDoc} */
559     @Override
560     public SparseGradient toDegrees() {
561         return new SparseGradient(FastMath.toDegrees(value), FastMath.toDegrees(1.0), derivatives);
562     }
563 
564     /** {@inheritDoc} */
565     @Override
566     public SparseGradient toRadians() {
567         return new SparseGradient(FastMath.toRadians(value), FastMath.toRadians(1.0), derivatives);
568     }
569 
570     /** Evaluate Taylor expansion of a sparse gradient.
571      * @param delta parameters offsets (&Delta;x, &Delta;y, ...)
572      * @return value of the Taylor expansion at x + &Delta;x, y + &Delta;y, ...
573      */
574     public double taylor(final double ... delta) {
575         double y = value;
576         for (int i = 0; i < delta.length; ++i) {
577             y += delta[i] * getDerivative(i);
578         }
579         return y;
580     }
581 
582     /** Compute composition of the instance by a univariate function.
583      * @param f array of value and derivatives of the function at
584      * the current point (i.e. [f({@link #getValue()}),
585      * f'({@link #getValue()}), f''({@link #getValue()})...]).
586      * @return f(this)
587      * @exception MathIllegalArgumentException if the number of elements
588      * in the array is not equal to 2 (i.e. value and first derivative)
589      */
590     @Override
591     public SparseGradient compose(final double... f) {
592         MathUtils.checkDimension(f.length, 2);
593         return compose(f[0], f[1]);
594     }
595 
596     /** {@inheritDoc} */
597     @Override
598     public SparseGradient compose(final double f0, final double f1) {
599         return new SparseGradient(f0, f1, derivatives);
600     }
601 
602     /** {@inheritDoc} */
603     @Override
604     public SparseGradient linearCombination(final SparseGradient[] a,
605                                             final SparseGradient[] b)
606         throws MathIllegalArgumentException {
607 
608         // compute a simple value, with all l derivatives
609         SparseGradient out = a[0].getField().getZero();
610         for (int i = 0; i < a.length; ++i) {
611             out = out.add(a[i].multiply(b[i]));
612         }
613 
614         // recompute an accurate value, taking care of cancellations
615         final double[] aDouble = new double[a.length];
616         for (int i = 0; i < a.length; ++i) {
617             aDouble[i] = a[i].getValue();
618         }
619         final double[] bDouble = new double[b.length];
620         for (int i = 0; i < b.length; ++i) {
621             bDouble[i] = b[i].getValue();
622         }
623         out.value = MathArrays.linearCombination(aDouble, bDouble);
624 
625         return out;
626 
627     }
628 
629     /** {@inheritDoc} */
630     @Override
631     public SparseGradient linearCombination(final double[] a, final SparseGradient[] b) {
632 
633         // compute a simple value, with all partial derivatives
634         SparseGradient out = b[0].getField().getZero();
635         for (int i = 0; i < a.length; ++i) {
636             out = out.add(b[i].multiply(a[i]));
637         }
638 
639         // recompute an accurate value, taking care of cancellations
640         final double[] bDouble = new double[b.length];
641         for (int i = 0; i < b.length; ++i) {
642             bDouble[i] = b[i].getValue();
643         }
644         out.value = MathArrays.linearCombination(a, bDouble);
645 
646         return out;
647 
648     }
649 
650     /** {@inheritDoc} */
651     @Override
652     public SparseGradient linearCombination(final SparseGradient a1, final SparseGradient b1,
653                                               final SparseGradient a2, final SparseGradient b2) {
654 
655         // compute a simple value, with all partial derivatives
656         SparseGradient out = a1.multiply(b1).add(a2.multiply(b2));
657 
658         // recompute an accurate value, taking care of cancellations
659         out.value = MathArrays.linearCombination(a1.value, b1.value, a2.value, b2.value);
660 
661         return out;
662 
663     }
664 
665     /** {@inheritDoc} */
666     @Override
667     public SparseGradient linearCombination(final double a1, final SparseGradient b1,
668                                               final double a2, final SparseGradient b2) {
669 
670         // compute a simple value, with all partial derivatives
671         SparseGradient out = b1.multiply(a1).add(b2.multiply(a2));
672 
673         // recompute an accurate value, taking care of cancellations
674         out.value = MathArrays.linearCombination(a1, b1.value, a2, b2.value);
675 
676         return out;
677 
678     }
679 
680     /** {@inheritDoc} */
681     @Override
682     public SparseGradient linearCombination(final SparseGradient a1, final SparseGradient b1,
683                                               final SparseGradient a2, final SparseGradient b2,
684                                               final SparseGradient a3, final SparseGradient b3) {
685 
686         // compute a simple value, with all partial derivatives
687         SparseGradient out = a1.multiply(b1).add(a2.multiply(b2)).add(a3.multiply(b3));
688 
689         // recompute an accurate value, taking care of cancellations
690         out.value = MathArrays.linearCombination(a1.value, b1.value,
691                                                  a2.value, b2.value,
692                                                  a3.value, b3.value);
693 
694         return out;
695 
696     }
697 
698     /** {@inheritDoc} */
699     @Override
700     public SparseGradient linearCombination(final double a1, final SparseGradient b1,
701                                               final double a2, final SparseGradient b2,
702                                               final double a3, final SparseGradient b3) {
703 
704         // compute a simple value, with all partial derivatives
705         SparseGradient out = b1.multiply(a1).add(b2.multiply(a2)).add(b3.multiply(a3));
706 
707         // recompute an accurate value, taking care of cancellations
708         out.value = MathArrays.linearCombination(a1, b1.value,
709                                                  a2, b2.value,
710                                                  a3, b3.value);
711 
712         return out;
713 
714     }
715 
716     /** {@inheritDoc} */
717     @Override
718     public SparseGradient linearCombination(final SparseGradient a1, final SparseGradient b1,
719                                               final SparseGradient a2, final SparseGradient b2,
720                                               final SparseGradient a3, final SparseGradient b3,
721                                               final SparseGradient a4, final SparseGradient b4) {
722 
723         // compute a simple value, with all partial derivatives
724         SparseGradient out = a1.multiply(b1).add(a2.multiply(b2)).add(a3.multiply(b3)).add(a4.multiply(b4));
725 
726         // recompute an accurate value, taking care of cancellations
727         out.value = MathArrays.linearCombination(a1.value, b1.value,
728                                                  a2.value, b2.value,
729                                                  a3.value, b3.value,
730                                                  a4.value, b4.value);
731 
732         return out;
733 
734     }
735 
736     /** {@inheritDoc} */
737     @Override
738     public SparseGradient linearCombination(final double a1, final SparseGradient b1,
739                                               final double a2, final SparseGradient b2,
740                                               final double a3, final SparseGradient b3,
741                                               final double a4, final SparseGradient b4) {
742 
743         // compute a simple value, with all partial derivatives
744         SparseGradient out = b1.multiply(a1).add(b2.multiply(a2)).add(b3.multiply(a3)).add(b4.multiply(a4));
745 
746         // recompute an accurate value, taking care of cancellations
747         out.value = MathArrays.linearCombination(a1, b1.value,
748                                                  a2, b2.value,
749                                                  a3, b3.value,
750                                                  a4, b4.value);
751 
752         return out;
753 
754     }
755 
756     /** {@inheritDoc} */
757     @Override
758     public SparseGradient getPi() {
759         return new SparseGradient(FastMath.PI, null);
760     }
761 
762     /**
763      * Test for the equality of two sparse gradients.
764      * <p>
765      * Sparse gradients are considered equal if they have the same value
766      * and the same derivatives.
767      * </p>
768      * @param other Object to test for equality to this
769      * @return true if two sparse gradients are equal
770      */
771     @Override
772     public boolean equals(Object other) {
773 
774         if (this == other) {
775             return true;
776         }
777 
778         if (other instanceof SparseGradient) {
779             final SparseGradient rhs = (SparseGradient)other;
780             if (!Precision.equals(value, rhs.value, 1)) {
781                 return false;
782             }
783             if (derivatives.size() != rhs.derivatives.size()) {
784                 return false;
785             }
786             for (final Map.Entry<Integer, Double> entry : derivatives.entrySet()) {
787                 if (!rhs.derivatives.containsKey(entry.getKey())) {
788                     return false;
789                 }
790                 if (!Precision.equals(entry.getValue(), rhs.derivatives.get(entry.getKey()), 1)) {
791                     return false;
792                 }
793             }
794             return true;
795         }
796 
797         return false;
798 
799     }
800 
801     /**
802      * Get a hashCode for the derivative structure.
803      * @return a hash code value for this object
804      */
805     @Override
806     public int hashCode() {
807         return 743 + 809 * MathUtils.hash(value) + 167 * derivatives.hashCode();
808     }
809 
810 }