View Javadoc
1   /*
2    * Licensed to the Hipparchus project under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The Hipparchus project licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.hipparchus.analysis.differentiation;
18  
19  import java.io.Serializable;
20  import java.util.Arrays;
21  
22  import org.hipparchus.exception.LocalizedCoreFormats;
23  import org.hipparchus.exception.MathIllegalArgumentException;
24  import org.hipparchus.util.FastMath;
25  import org.hipparchus.util.FieldSinCos;
26  import org.hipparchus.util.FieldSinhCosh;
27  import org.hipparchus.util.MathArrays;
28  import org.hipparchus.util.MathUtils;
29  import org.hipparchus.util.SinCos;
30  import org.hipparchus.util.SinhCosh;
31  
32  /** Class representing both the value and the differentials of a function.
33   * <p>This class is a stripped-down version of {@link DerivativeStructure}
34   * with {@link DerivativeStructure#getOrder() derivation order} limited to one.
35   * It should have less overhead than {@link DerivativeStructure} in its domain.</p>
36   * <p>This class is an implementation of Rall's numbers. Rall's numbers are an
37   * extension to the real numbers used throughout mathematical expressions; they hold
38   * the derivative together with the value of a function.</p>
39   * <p>{@link Gradient} instances can be used directly thanks to
40   * the arithmetic operators to the mathematical functions provided as
41   * methods by this class (+, -, *, /, %, sin, cos ...).</p>
42   * <p>Implementing complex expressions by hand using these classes is
43   * a tedious and error-prone task but has the advantage of having no limitation
44   * on the derivation order despite not requiring users to compute the derivatives by
45   * themselves.</p>
46   * <p>Instances of this class are guaranteed to be immutable.</p>
47   * @see DerivativeStructure
48   * @see UnivariateDerivative1
49   * @see UnivariateDerivative2
50   * @see FieldDerivativeStructure
51   * @see FieldUnivariateDerivative1
52   * @see FieldUnivariateDerivative2
53   * @see FieldGradient
54   * @since 1.7
55   */
56  public class Gradient implements Derivative1<Gradient>, Serializable {
57  
58      /** Serializable UID. */
59      private static final long serialVersionUID = 20200520L;
60  
61      /** Value of the function. */
62      private final double value;
63  
64      /** Gradient of the function. */
65      private final double[] grad;
66  
67      /** Build an instance with values and uninitialized derivatives array.
68       * @param value value of the function
69       * @param freeParameters number of free parameters
70       */
71      private Gradient(final double value, int freeParameters) {
72          this.value = value;
73          this.grad  = new double[freeParameters];
74      }
75  
76      /** Build an instance with value and derivatives array, used for performance internally (no copy).
77       * @param value value of the function
78       * @param gradient derivatives
79       * @since 3.1
80       */
81      private Gradient(final double[] gradient, final double value) {
82          this.value = value;
83          this.grad  = gradient;
84      }
85  
86      /** Build an instance with values and derivative.
87       * @param value value of the function
88       * @param gradient gradient of the function
89       */
90      public Gradient(final double value, final double... gradient) {
91          this(value, gradient.length);
92          System.arraycopy(gradient, 0, grad, 0, grad.length);
93      }
94  
95      /** Build an instance from a {@link DerivativeStructure}.
96       * @param ds derivative structure
97       * @exception MathIllegalArgumentException if {@code ds} order
98       * is not 1
99       */
100     public Gradient(final DerivativeStructure ds) throws MathIllegalArgumentException {
101         this(ds.getValue(), ds.getFreeParameters());
102         MathUtils.checkDimension(ds.getOrder(), 1);
103         System.arraycopy(ds.getAllDerivatives(), 1, grad, 0, grad.length);
104     }
105 
106     /** Build an instance corresponding to a constant value.
107      * @param freeParameters number of free parameters (i.e. dimension of the gradient)
108      * @param value constant value of the function
109      * @return a {@code Gradient} with a constant value and all derivatives set to 0.0
110      */
111     public static Gradient constant(final int freeParameters, final double value) {
112         return new Gradient(value, freeParameters);
113     }
114 
115     /** Build a {@code Gradient} representing a variable.
116      * <p>Instances built using this method are considered
117      * to be the free variables with respect to which differentials
118      * are computed. As such, their differential with respect to
119      * themselves is +1.</p>
120      * @param freeParameters number of free parameters (i.e. dimension of the gradient)
121      * @param index index of the variable (from 0 to {@link #getFreeParameters() getFreeParameters()} - 1)
122      * @param value value of the variable
123      * @return a {@code Gradient} with a constant value and all derivatives set to 0.0 except the
124      * one at {@code index} which will be set to 1.0
125      */
126     public static Gradient variable(final int freeParameters, final int index, final double value) {
127         final Gradient g = new Gradient(value, freeParameters);
128         g.grad[index] = 1.0;
129         return g;
130     }
131 
132     /** {@inheritDoc} */
133     @Override
134     public Gradient newInstance(final double c) {
135         return new Gradient(c, new double[grad.length]);
136     }
137 
138     /** {@inheritDoc} */
139     @Override
140     public Gradient withValue(final double v) {
141         return new Gradient(v, grad);
142     }
143 
144     /** Get the value part of the function.
145      * @return value part of the value of the function
146      */
147     @Override
148     public double getValue() {
149         return value;
150     }
151 
152     /** Get the gradient part of the function.
153      * @return gradient part of the value of the function
154      * @see #getPartialDerivative(int)
155      */
156     public double[] getGradient() {
157         return grad.clone();
158     }
159 
160     /** {@inheritDoc} */
161     @Override
162     public int getFreeParameters() {
163         return grad.length;
164     }
165 
166     /** {@inheritDoc} */
167     @Override
168     public double getPartialDerivative(final int ... orders)
169             throws MathIllegalArgumentException {
170 
171         // check the number of components
172         if (orders.length != grad.length) {
173             throw new MathIllegalArgumentException(LocalizedCoreFormats.DIMENSIONS_MISMATCH,
174                     orders.length, grad.length);
175         }
176 
177         // check that either all derivation orders are set to 0,
178         // or that only one is set to 1 and all other ones are set to 0
179         int selected = -1;
180         for (int i = 0; i < orders.length; ++i) {
181             if (orders[i] != 0) {
182                 if (selected >= 0 || orders[i] != 1) {
183                     throw new MathIllegalArgumentException(LocalizedCoreFormats.DERIVATION_ORDER_NOT_ALLOWED,
184                             orders[i]);
185                 }
186                 // found the component set to derivation order 1
187                 selected = i;
188             }
189         }
190 
191         return (selected < 0) ? value : grad[selected];
192 
193     }
194 
195     /** Get the partial derivative with respect to one parameter.
196      * @param n index of the parameter (counting from 0)
197      * @return partial derivative with respect to the n<sup>th</sup> parameter
198      * @exception MathIllegalArgumentException if n is either negative or larger
199      * or equal to {@link #getFreeParameters()}
200      */
201     public double getPartialDerivative(final int n) throws MathIllegalArgumentException {
202         if (n < 0 || n >= grad.length) {
203             throw new MathIllegalArgumentException(LocalizedCoreFormats.OUT_OF_RANGE_SIMPLE, n, 0, grad.length - 1);
204         }
205         return grad[n];
206     }
207 
208     /** Convert the instance to a {@link DerivativeStructure}.
209      * @return derivative structure with same value and derivative as the instance
210      */
211     public DerivativeStructure toDerivativeStructure() {
212         final double[] derivatives = new double[1 + grad.length];
213         derivatives[0] = value;
214         System.arraycopy(grad, 0, derivatives, 1, grad.length);
215         return getField().getConversionFactory().build(derivatives);
216     }
217 
218     /** {@inheritDoc} */
219     @Override
220     public Gradient add(final Gradient a) {
221         final double[] gradient = new double[grad.length];
222         for (int i = 0; i < grad.length; ++i) {
223             gradient[i] = grad[i] + a.grad[i];
224         }
225         return new Gradient(gradient, value + a.value);
226     }
227 
228     /** {@inheritDoc} */
229     @Override
230     public Gradient subtract(final Gradient a) {
231         final double[] gradient = new double[grad.length];
232         for (int i = 0; i < grad.length; ++i) {
233             gradient[i] = grad[i] - a.grad[i];
234         }
235         return new Gradient(gradient, value - a.value);
236     }
237 
238     /** {@inheritDoc} */
239     @Override
240     public Gradient multiply(final int n) {
241         final double[] gradient = new double[grad.length];
242         for (int i = 0; i < grad.length; ++i) {
243             gradient[i] = grad[i] * n;
244         }
245         return new Gradient(gradient, value * n);
246     }
247 
248     /** {@inheritDoc} */
249     @Override
250     public Gradient multiply(final double a) {
251         final double[] gradient = new double[grad.length];
252         for (int i = 0; i < grad.length; ++i) {
253             gradient[i] = grad[i] * a;
254         }
255         return new Gradient(gradient, value * a);
256     }
257 
258     /** {@inheritDoc} */
259     @Override
260     public Gradient multiply(final Gradient a) {
261         final double[] gradient = new double[grad.length];
262         for (int i = 0; i < grad.length; ++i) {
263             gradient[i] = grad[i] * a.value + value * a.grad[i];
264         }
265         return new Gradient(gradient, value * a.value);
266     }
267 
268     /** {@inheritDoc} */
269     @Override
270     public Gradient divide(final double a) {
271         final double[] gradient = new double[grad.length];
272         for (int i = 0; i < grad.length; ++i) {
273             gradient[i] = grad[i] / a;
274         }
275         return new Gradient(gradient, value / a);
276     }
277 
278     /** {@inheritDoc} */
279     @Override
280     public Gradient divide(final Gradient a) {
281         final double inv1 = 1.0 / a.value;
282         final double inv2 = inv1 * inv1;
283         final double[] gradient = new double[grad.length];
284         for (int i = 0; i < grad.length; ++i) {
285             gradient[i] = (grad[i] * a.value - value * a.grad[i]) * inv2;
286         }
287         return new Gradient(gradient, value * inv1);
288     }
289 
290     /** {@inheritDoc} */
291     @Override
292     public Gradient remainder(final Gradient a) {
293 
294         // compute k such that lhs % rhs = lhs - k rhs
295         final double rem = FastMath.IEEEremainder(value, a.value);
296         final double k   = FastMath.rint((value - rem) / a.value);
297 
298         final double[] gradient = new double[grad.length];
299         for (int i = 0; i < grad.length; ++i) {
300             gradient[i] = grad[i] - k * a.grad[i];
301         }
302         return new Gradient(gradient, rem);
303 
304     }
305 
306     /** {@inheritDoc} */
307     @Override
308     public Gradient negate() {
309         final double[] gradient = new double[grad.length];
310         for (int i = 0; i < grad.length; ++i) {
311             gradient[i] = -grad[i];
312         }
313         return new Gradient(gradient, -value);
314     }
315 
316     /** {@inheritDoc} */
317     @Override
318     public Gradient abs() {
319         if (Double.doubleToLongBits(value) < 0) {
320             // we use the bits representation to also handle -0.0
321             return negate();
322         } else {
323             return this;
324         }
325     }
326 
327     /** {@inheritDoc} */
328     @Override
329     public Gradient copySign(final Gradient sign) {
330         long m = Double.doubleToLongBits(value);
331         long s = Double.doubleToLongBits(sign.value);
332         if ((m >= 0 && s >= 0) || (m < 0 && s < 0)) { // Sign is currently OK
333             return this;
334         }
335         return negate(); // flip sign
336     }
337 
338     /** {@inheritDoc} */
339     @Override
340     public Gradient copySign(final double sign) {
341         long m = Double.doubleToLongBits(value);
342         long s = Double.doubleToLongBits(sign);
343         if ((m >= 0 && s >= 0) || (m < 0 && s < 0)) { // Sign is currently OK
344             return this;
345         }
346         return negate(); // flip sign
347     }
348 
349     /** {@inheritDoc} */
350     @Override
351     public Gradient scalb(final int n) {
352         final double[] gradient = new double[grad.length];
353         for (int i = 0; i < grad.length; ++i) {
354             gradient[i] = FastMath.scalb(grad[i], n);
355         }
356         return new Gradient(gradient, FastMath.scalb(value, n));
357     }
358 
359     /** {@inheritDoc} */
360     @Override
361     public Gradient hypot(final Gradient y) {
362 
363         if (Double.isInfinite(value) || Double.isInfinite(y.value)) {
364             return newInstance(Double.POSITIVE_INFINITY);
365         } else if (Double.isNaN(value) || Double.isNaN(y.value)) {
366             return newInstance(Double.NaN);
367         } else {
368 
369             final int expX = getExponent();
370             final int expY = y.getExponent();
371             if (expX > expY + 27) {
372                 // y is negligible with respect to x
373                 return abs();
374             } else if (expY > expX + 27) {
375                 // x is negligible with respect to y
376                 return y.abs();
377             } else {
378 
379                 // find an intermediate scale to avoid both overflow and underflow
380                 final int middleExp = (expX + expY) / 2;
381 
382                 // scale parameters without losing precision
383                 final Gradient scaledX = scalb(-middleExp);
384                 final Gradient scaledY = y.scalb(-middleExp);
385 
386                 // compute scaled hypotenuse
387                 final Gradient scaledH =
388                         scaledX.multiply(scaledX).add(scaledY.multiply(scaledY)).sqrt();
389 
390                 // remove scaling
391                 return scaledH.scalb(middleExp);
392 
393             }
394 
395         }
396     }
397 
398     /** {@inheritDoc} */
399     @Override
400     public Gradient compose(final double... f) {
401         MathUtils.checkDimension(f.length, getOrder() + 1);
402         return compose(f[0], f[1]);
403     }
404 
405     /** {@inheritDoc} */
406     @Override
407     public Gradient compose(final double f0, final double f1) {
408         final double[] gradient = new double[grad.length];
409         for (int i = 0; i < grad.length; ++i) {
410             gradient[i] = f1 * grad[i];
411         }
412         return new Gradient(gradient, f0);
413     }
414 
415     /** {@inheritDoc} */
416     @Override
417     public GradientField getField() {
418         return GradientField.getField(getFreeParameters());
419     }
420 
421     /** Compute a<sup>x</sup> where a is a double and x a {@link Gradient}
422      * @param a number to exponentiate
423      * @param x power to apply
424      * @return a<sup>x</sup>
425      */
426     public static Gradient pow(final double a, final Gradient x) {
427         if (a == 0) {
428             return x.getField().getZero();
429         } else {
430             final double aX = FastMath.pow(a, x.value);
431             final double aXlnA = aX * FastMath.log(a);
432             final double[] gradient = new double[x.getFreeParameters()];
433             for (int i = 0; i < gradient.length; ++i) {
434                 gradient[i] =  aXlnA * x.grad[i];
435             }
436             return new Gradient(gradient, aX);
437         }
438     }
439 
440     /** {@inheritDoc} */
441     @Override
442     public Gradient pow(final double p) {
443         if (p == 0) {
444             return getField().getOne();
445         } else {
446             final double valuePm1 = FastMath.pow(value, p - 1);
447             return compose(valuePm1 * value, p * valuePm1);
448         }
449     }
450 
451     /** {@inheritDoc} */
452     @Override
453     public Gradient pow(final int n) {
454         if (n == 0) {
455             return getField().getOne();
456         } else {
457             final double valueNm1 = FastMath.pow(value, n - 1);
458             return compose(valueNm1 * value, n * valueNm1);
459         }
460     }
461 
462     /** {@inheritDoc} */
463     @Override
464     public FieldSinCos<Gradient> sinCos() {
465         final SinCos sinCos = FastMath.sinCos(value);
466         final double[] gradSin = new double[grad.length];
467         final double[] gradCos = new double[grad.length];
468         for (int i = 0; i < grad.length; ++i) {
469             gradSin[i] =  grad[i] * sinCos.cos();
470             gradCos[i] =  -grad[i] * sinCos.sin();
471         }
472         final Gradient sin = new Gradient(gradSin, sinCos.sin());
473         final Gradient cos = new Gradient(gradCos, sinCos.cos());
474         return new FieldSinCos<>(sin, cos);
475     }
476 
477     /** {@inheritDoc} */
478     @Override
479     public Gradient atan2(final Gradient x) {
480         final double inv = 1.0 / (value * value + x.value * x.value);
481         final double[] gradient = new double[grad.length];
482         for (int i = 0; i < grad.length; ++i) {
483             gradient[i] = (x.value * grad[i] - x.grad[i] * value) * inv;
484         }
485         return new Gradient(gradient, FastMath.atan2(value, x.value));
486     }
487 
488     /** {@inheritDoc} */
489     @Override
490     public FieldSinhCosh<Gradient> sinhCosh() {
491         final SinhCosh sinhCosh = FastMath.sinhCosh(value);
492         final double[] gradSinh = new double[grad.length];
493         final double[] gradCosh = new double[grad.length];
494         for (int i = 0; i < grad.length; ++i) {
495             gradSinh[i] = grad[i] * sinhCosh.cosh();
496             gradCosh[i] = grad[i] * sinhCosh.sinh();
497         }
498         final Gradient sinh = new Gradient(gradSinh, sinhCosh.sinh());
499         final Gradient cosh = new Gradient(gradCosh, sinhCosh.cosh());
500         return new FieldSinhCosh<>(sinh, cosh);
501     }
502 
503     /** {@inheritDoc} */
504     @Override
505     public Gradient toDegrees() {
506         final double[] gradient = new double[grad.length];
507         for (int i = 0; i < grad.length; ++i) {
508             gradient[i] = FastMath.toDegrees(grad[i]);
509         }
510         return new Gradient(gradient, FastMath.toDegrees(value));
511     }
512 
513     /** {@inheritDoc} */
514     @Override
515     public Gradient toRadians() {
516         final double[] gradient = new double[grad.length];
517         for (int i = 0; i < grad.length; ++i) {
518             gradient[i] = FastMath.toRadians(grad[i]);
519         }
520         return new Gradient(gradient, FastMath.toRadians(value));
521     }
522 
523     /** Evaluate Taylor expansion a derivative structure.
524      * @param delta parameters offsets (&Delta;x, &Delta;y, ...)
525      * @return value of the Taylor expansion at x + &Delta;x, y + &Delta;y, ...
526      */
527     public double taylor(final double... delta) {
528         double result = value;
529         for (int i = 0; i < grad.length; ++i) {
530             result += delta[i] * grad[i];
531         }
532         return result;
533     }
534 
535     /** {@inheritDoc} */
536     @Override
537     public Gradient linearCombination(final Gradient[] a, final Gradient[] b) {
538 
539         // extract values and first derivatives
540         final int      n  = a.length;
541         final double[] a0 = new double[n];
542         final double[] b0 = new double[n];
543         final double[] a1 = new double[2 * n];
544         final double[] b1 = new double[2 * n];
545         for (int i = 0; i < n; ++i) {
546             final Gradient ai = a[i];
547             final Gradient bi = b[i];
548             a0[i]         = ai.value;
549             b0[i]         = bi.value;
550             a1[2 * i]     = ai.value;
551             b1[2 * i + 1] = bi.value;
552         }
553 
554         final Gradient result = newInstance(MathArrays.linearCombination(a0, b0));
555         for (int k = 0; k < grad.length; ++k) {
556             for (int i = 0; i < n; ++i) {
557                 a1[2 * i + 1] = a[i].grad[k];
558                 b1[2 * i]     = b[i].grad[k];
559             }
560             result.grad[k] = MathArrays.linearCombination(a1, b1);
561         }
562         return result;
563 
564     }
565 
566     /** {@inheritDoc} */
567     @Override
568     public Gradient linearCombination(final double[] a, final Gradient[] b) {
569 
570         // extract values and first derivatives
571         final int      n  = b.length;
572         final double[] b0 = new double[n];
573         final double[] b1 = new double[n];
574         for (int i = 0; i < n; ++i) {
575             b0[i] = b[i].value;
576         }
577 
578         final Gradient result = newInstance(MathArrays.linearCombination(a, b0));
579         for (int k = 0; k < grad.length; ++k) {
580             for (int i = 0; i < n; ++i) {
581                 b1[i] = b[i].grad[k];
582             }
583             result.grad[k] = MathArrays.linearCombination(a, b1);
584         }
585         return result;
586 
587     }
588 
589     /** {@inheritDoc} */
590     @Override
591     public Gradient linearCombination(final Gradient a1, final Gradient b1,
592                                       final Gradient a2, final Gradient b2) {
593         final Gradient result = newInstance(MathArrays.linearCombination(a1.value, b1.value,
594                 a2.value, b2.value));
595         for (int i = 0; i < b1.grad.length; ++i) {
596             result.grad[i] = MathArrays.linearCombination(a1.value,       b1.grad[i],
597                     a1.grad[i], b1.value,
598                     a2.value,       b2.grad[i],
599                     a2.grad[i], b2.value);
600         }
601         return result;
602     }
603 
604     /** {@inheritDoc} */
605     @Override
606     public Gradient linearCombination(final double a1, final Gradient b1,
607                                       final double a2, final Gradient b2) {
608         final Gradient result = newInstance(MathArrays.linearCombination(a1, b1.value,
609                 a2, b2.value));
610         for (int i = 0; i < b1.grad.length; ++i) {
611             result.grad[i] = MathArrays.linearCombination(a1, b1.grad[i],
612                     a2, b2.grad[i]);
613         }
614         return result;
615     }
616 
617     /** {@inheritDoc} */
618     @Override
619     public Gradient linearCombination(final Gradient a1, final Gradient b1,
620                                       final Gradient a2, final Gradient b2,
621                                       final Gradient a3, final Gradient b3) {
622         final double[] a = {
623                 a1.value, 0, a2.value, 0, a3.value, 0
624         };
625         final double[] b = {
626                 0, b1.value, 0, b2.value, 0, b3.value
627         };
628         final Gradient result = newInstance(MathArrays.linearCombination(a1.value, b1.value,
629                 a2.value, b2.value,
630                 a3.value, b3.value));
631         for (int i = 0; i < b1.grad.length; ++i) {
632             a[1] = a1.grad[i];
633             a[3] = a2.grad[i];
634             a[5] = a3.grad[i];
635             b[0] = b1.grad[i];
636             b[2] = b2.grad[i];
637             b[4] = b3.grad[i];
638             result.grad[i] = MathArrays.linearCombination(a, b);
639         }
640         return result;
641     }
642 
643     /** {@inheritDoc} */
644     @Override
645     public Gradient linearCombination(final double a1, final Gradient b1,
646                                       final double a2, final Gradient b2,
647                                       final double a3, final Gradient b3) {
648         final Gradient result = newInstance(MathArrays.linearCombination(a1, b1.value,
649                 a2, b2.value,
650                 a3, b3.value));
651         for (int i = 0; i < b1.grad.length; ++i) {
652             result.grad[i] = MathArrays.linearCombination(a1, b1.grad[i],
653                     a2, b2.grad[i],
654                     a3, b3.grad[i]);
655         }
656         return result;
657     }
658 
659     /** {@inheritDoc} */
660     @Override
661     public Gradient linearCombination(final Gradient a1, final Gradient b1,
662                                       final Gradient a2, final Gradient b2,
663                                       final Gradient a3, final Gradient b3,
664                                       final Gradient a4, final Gradient b4) {
665         final double[] a = {
666                 a1.value, 0, a2.value, 0, a3.value, 0, a4.value, 0
667         };
668         final double[] b = {
669                 0, b1.value, 0, b2.value, 0, b3.value, 0, b4.value
670         };
671         final Gradient result = newInstance(MathArrays.linearCombination(a1.value, b1.value,
672                 a2.value, b2.value,
673                 a3.value, b3.value,
674                 a4.value, b4.value));
675         for (int i = 0; i < b1.grad.length; ++i) {
676             a[1] = a1.grad[i];
677             a[3] = a2.grad[i];
678             a[5] = a3.grad[i];
679             a[7] = a4.grad[i];
680             b[0] = b1.grad[i];
681             b[2] = b2.grad[i];
682             b[4] = b3.grad[i];
683             b[6] = b4.grad[i];
684             result.grad[i] = MathArrays.linearCombination(a, b);
685         }
686         return result;
687     }
688 
689     /** {@inheritDoc} */
690     @Override
691     public Gradient linearCombination(final double a1, final Gradient b1,
692                                       final double a2, final Gradient b2,
693                                       final double a3, final Gradient b3,
694                                       final double a4, final Gradient b4) {
695         final Gradient result = newInstance(MathArrays.linearCombination(a1, b1.value,
696                 a2, b2.value,
697                 a3, b3.value,
698                 a4, b4.value));
699         for (int i = 0; i < b1.grad.length; ++i) {
700             result.grad[i] = MathArrays.linearCombination(a1, b1.grad[i],
701                     a2, b2.grad[i],
702                     a3, b3.grad[i],
703                     a4, b4.grad[i]);
704         }
705         return result;
706     }
707 
708     /** Test for the equality of two univariate derivatives.
709      * <p>
710      * univariate derivatives are considered equal if they have the same derivatives.
711      * </p>
712      * @param other Object to test for equality to this
713      * @return true if two univariate derivatives are equal
714      */
715     @Override
716     public boolean equals(Object other) {
717 
718         if (this == other) {
719             return true;
720         }
721 
722         if (other instanceof Gradient) {
723             final Gradient rhs = (Gradient) other;
724             return value == rhs.value && MathArrays.equals(grad, rhs.grad);
725         }
726 
727         return false;
728 
729     }
730 
731     /** Get a hashCode for the univariate derivative.
732      * @return a hash code value for this object
733      */
734     @Override
735     public int hashCode() {
736         return 129 + 7 * Double.hashCode(value) - 15 * Arrays.hashCode(grad);
737     }
738 
739 }