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