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.util.Arrays;
20  
21  import org.hipparchus.CalculusFieldElement;
22  import org.hipparchus.Field;
23  import org.hipparchus.exception.LocalizedCoreFormats;
24  import org.hipparchus.exception.MathIllegalArgumentException;
25  import org.hipparchus.util.FastMath;
26  import org.hipparchus.util.FieldSinCos;
27  import org.hipparchus.util.FieldSinhCosh;
28  import org.hipparchus.util.MathArrays;
29  import org.hipparchus.util.MathUtils;
30  
31  /** Class representing both the value and the differentials of a function.
32   * <p>This class is a stripped-down version of {@link FieldDerivativeStructure}
33   * with {@link FieldDerivativeStructure#getOrder() derivation order} limited to one.
34   * It should have less overhead than {@link FieldDerivativeStructure} in its domain.</p>
35   * <p>This class is an implementation of Rall's numbers. Rall's numbers are an
36   * extension to the real numbers used throughout mathematical expressions; they hold
37   * the derivative together with the value of a function.</p>
38   * <p>{@link FieldGradient} instances can be used directly thanks to
39   * the arithmetic operators to the mathematical functions provided as
40   * methods by this class (+, -, *, /, %, sin, cos ...).</p>
41   * <p>Implementing complex expressions by hand using these classes is
42   * a tedious and error-prone task but has the advantage of having no limitation
43   * on the derivation order despite not requiring users to compute the derivatives by
44   * themselves.</p>
45   * <p>Instances of this class are guaranteed to be immutable.</p>
46   * @param <T> the type of the function parameters and value
47   * @see DerivativeStructure
48   * @see UnivariateDerivative1
49   * @see UnivariateDerivative2
50   * @see Gradient
51   * @see FieldDerivativeStructure
52   * @see FieldUnivariateDerivative1
53   * @see FieldUnivariateDerivative2
54   * @since 1.7
55   */
56  public class FieldGradient<T extends CalculusFieldElement<T>> implements FieldDerivative1<T, FieldGradient<T>> {
57  
58      /** Value of the function. */
59      private final T value;
60  
61      /** Gradient of the function. */
62      private final T[] grad;
63  
64      /** Build an instance with values and unitialized derivatives array.
65       * @param value value of the function
66       * @param freeParameters number of free parameters
67       */
68      private FieldGradient(final T value, int freeParameters) {
69          this.value = value;
70          this.grad  = MathArrays.buildArray(value.getField(), freeParameters);
71      }
72  
73      /** Build an instance with values and derivative.
74       * @param value value of the function
75       * @param gradient gradient of the function
76       */
77      @SafeVarargs
78      public FieldGradient(final T value, final T... gradient) {
79          this(value, gradient.length);
80          System.arraycopy(gradient, 0, grad, 0, grad.length);
81      }
82  
83      /** Build an instance from a {@link DerivativeStructure}.
84       * @param ds derivative structure
85       * @exception MathIllegalArgumentException if {@code ds} order
86       * is not 1
87       */
88      public FieldGradient(final FieldDerivativeStructure<T> ds) throws MathIllegalArgumentException {
89          this(ds.getValue(), ds.getFreeParameters());
90          MathUtils.checkDimension(ds.getOrder(), 1);
91          System.arraycopy(ds.getAllDerivatives(), 1, grad, 0, grad.length);
92      }
93  
94      /** Build an instance corresponding to a constant value.
95       * @param freeParameters number of free parameters (i.e. dimension of the gradient)
96       * @param value constant value of the function
97       * @param <T> the type of the function parameters and value
98       * @return a {@code FieldGradient} with a constant value and all derivatives set to 0.0
99       */
100     public static <T extends CalculusFieldElement<T>> FieldGradient<T> constant(final int freeParameters, final T value) {
101         final FieldGradient<T> g = new FieldGradient<>(value, freeParameters);
102         Arrays.fill(g.grad, value.getField().getZero());
103         return g;
104     }
105 
106     /** Build a {@code Gradient} representing a variable.
107      * <p>Instances built using this method are considered
108      * to be the free variables with respect to which differentials
109      * are computed. As such, their differential with respect to
110      * themselves is +1.</p>
111      * @param freeParameters number of free parameters (i.e. dimension of the gradient)
112      * @param index index of the variable (from 0 to {@link #getFreeParameters() getFreeParameters()} - 1)
113      * @param value value of the variable
114      * @param <T> the type of the function parameters and value
115      * @return a {@code FieldGradient} with a constant value and all derivatives set to 0.0 except the
116      * one at {@code index} which will be set to 1.0
117      */
118     public static <T extends CalculusFieldElement<T>> FieldGradient<T> variable(final int freeParameters,
119                                                                                 final int index, final T value) {
120         final FieldGradient<T> g = new FieldGradient<>(value, freeParameters);
121         final Field<T> field = value.getField();
122         Arrays.fill(g.grad, field.getZero());
123         g.grad[index] = field.getOne();
124         return g;
125     }
126 
127     /** Get the {@link Field} the value and parameters of the function belongs to.
128      * @return {@link Field} the value and parameters of the function belongs to
129      */
130     public Field<T> getValueField() {
131         return value.getField();
132     }
133 
134     /** {@inheritDoc} */
135     @Override
136     public FieldGradient<T> newInstance(final double c) {
137         return newInstance(getValueField().getZero().newInstance(c));
138     }
139 
140     /** {@inheritDoc} */
141     @Override
142     public FieldGradient<T> newInstance(final T c) {
143         return new FieldGradient<>(c, MathArrays.buildArray(value.getField(), grad.length));
144     }
145 
146     /** {@inheritDoc} */
147     @Override
148     public FieldGradient<T> withValue(final T v) {
149         return new FieldGradient<>(v, grad);
150     }
151 
152     /** Get the value part of the function.
153      * @return value part of the value of the function
154      */
155     @Override
156     public T getValue() {
157         return value;
158     }
159 
160     /** Get the gradient part of the function.
161      * @return gradient part of the value of the function
162      */
163     public T[] getGradient() {
164         return grad.clone();
165     }
166 
167     /** Get the number of free parameters.
168      * @return number of free parameters
169      */
170     @Override
171     public int getFreeParameters() {
172         return grad.length;
173     }
174 
175     /** {@inheritDoc} */
176     @Override
177     public T getPartialDerivative(final int ... orders)
178         throws MathIllegalArgumentException {
179 
180         // check the number of components
181         if (orders.length != grad.length) {
182             throw new MathIllegalArgumentException(LocalizedCoreFormats.DIMENSIONS_MISMATCH,
183                                                    orders.length, grad.length);
184         }
185 
186         // check that either all derivation orders are set to 0,
187         // or that only one is set to 1 and all other ones are set to 0
188         int selected = -1;
189         for (int i = 0; i < orders.length; ++i) {
190             if (orders[i] != 0) {
191                 if (selected >= 0 || orders[i] != 1) {
192                      throw new MathIllegalArgumentException(LocalizedCoreFormats.DERIVATION_ORDER_NOT_ALLOWED,
193                                                            orders[i]);
194                 }
195                 // found the component set to derivation order 1
196                 selected = i;
197             }
198         }
199 
200         return (selected < 0) ? value : grad[selected];
201 
202     }
203 
204     /** Get the partial derivative with respect to one parameter.
205      * @param n index of the parameter (counting from 0)
206      * @return partial derivative with respect to the n<sup>th</sup> parameter
207      * @exception MathIllegalArgumentException if n is either negative or larger
208      * or equal to {@link #getFreeParameters()}
209      */
210     public T getPartialDerivative(final int n) throws MathIllegalArgumentException {
211         if (n < 0 || n >= grad.length) {
212             throw new MathIllegalArgumentException(LocalizedCoreFormats.OUT_OF_RANGE_SIMPLE, n, 0, grad.length - 1);
213         }
214         return grad[n];
215     }
216 
217     /** Convert the instance to a {@link FieldDerivativeStructure}.
218      * @return derivative structure with same value and derivative as the instance
219      */
220     public FieldDerivativeStructure<T> toDerivativeStructure() {
221         final T[] derivatives = MathArrays.buildArray(getValueField(), 1 + grad.length);
222         derivatives[0] = value;
223         System.arraycopy(grad, 0, derivatives, 1, grad.length);
224         return getField().getConversionFactory().build(derivatives);
225     }
226 
227     /** {@inheritDoc} */
228     @Override
229     public FieldGradient<T> add(final double a) {
230         return new FieldGradient<>(value.add(a), grad);
231     }
232 
233     /** {@inheritDoc} */
234     @Override
235     public FieldGradient<T> add(final FieldGradient<T> a) {
236         final FieldGradient<T> result = newInstance(value.add(a.value));
237         for (int i = 0; i < grad.length; ++i) {
238             result.grad[i] = grad[i].add(a.grad[i]);
239         }
240         return result;
241     }
242 
243     /** {@inheritDoc} */
244     @Override
245     public FieldGradient<T> subtract(final double a) {
246         return new FieldGradient<>(value.subtract(a), grad);
247     }
248 
249     /** {@inheritDoc} */
250     @Override
251     public FieldGradient<T> subtract(final FieldGradient<T> a) {
252         final FieldGradient<T> result = newInstance(value.subtract(a.value));
253         for (int i = 0; i < grad.length; ++i) {
254             result.grad[i] = grad[i].subtract(a.grad[i]);
255         }
256         return result;
257     }
258 
259     /** '&times;' operator.
260      * @param n right hand side parameter of the operator
261      * @return this&times;n
262      */
263     public FieldGradient<T> multiply(final T n) {
264         final FieldGradient<T> result = newInstance(value.multiply(n));
265         for (int i = 0; i < grad.length; ++i) {
266             result.grad[i] = grad[i].multiply(n);
267         }
268         return result;
269     }
270 
271     /** {@inheritDoc} */
272     @Override
273     public FieldGradient<T> multiply(final int n) {
274         final FieldGradient<T> result = newInstance(value.multiply(n));
275         for (int i = 0; i < grad.length; ++i) {
276             result.grad[i] = grad[i].multiply(n);
277         }
278         return result;
279     }
280 
281     /** {@inheritDoc} */
282     @Override
283     public FieldGradient<T> multiply(final double a) {
284         final FieldGradient<T> result = newInstance(value.multiply(a));
285         for (int i = 0; i < grad.length; ++i) {
286             result.grad[i] = grad[i].multiply(a);
287         }
288         return result;
289     }
290 
291     /** {@inheritDoc} */
292     @Override
293     public FieldGradient<T> multiply(final FieldGradient<T> a) {
294         final FieldGradient<T> result = newInstance(value.multiply(a.value));
295         for (int i = 0; i < grad.length; ++i) {
296             result.grad[i] = grad[i].multiply(a.value).add(value.multiply(a.grad[i]));
297         }
298         return result;
299     }
300 
301     /** '&divide;' operator.
302      * @param a right hand side parameter of the operator
303      * @return this&divide;a
304      */
305     public FieldGradient<T> divide(final T a) {
306         final FieldGradient<T> result = newInstance(value.divide(a));
307         for (int i = 0; i < grad.length; ++i) {
308             result.grad[i] = grad[i].divide(a);
309         }
310         return result;
311     }
312 
313     /** {@inheritDoc} */
314     @Override
315     public FieldGradient<T> divide(final double a) {
316         final FieldGradient<T> result = newInstance(value.divide(a));
317         for (int i = 0; i < grad.length; ++i) {
318             result.grad[i] = grad[i].divide(a);
319         }
320         return result;
321     }
322 
323     /** {@inheritDoc} */
324     @Override
325     public FieldGradient<T> divide(final FieldGradient<T> a) {
326         final T inv1 = a.value.reciprocal();
327         final T inv2 = inv1.multiply(inv1);
328         final FieldGradient<T> result = newInstance(value.multiply(inv1));
329         for (int i = 0; i < grad.length; ++i) {
330             result.grad[i] = grad[i].multiply(a.value).subtract(value.multiply(a.grad[i])).multiply(inv2);
331         }
332         return result;
333     }
334 
335     /** IEEE remainder operator.
336      * @param a right hand side parameter of the operator
337      * @return this - n &times; a where n is the closest integer to this/a
338      * (the even integer is chosen for n if this/a is halfway between two integers)
339      */
340     public FieldGradient<T> remainder(final T a) {
341         return new FieldGradient<>(FastMath.IEEEremainder(value, a), grad);
342     }
343 
344     /** {@inheritDoc} */
345     @Override
346     public FieldGradient<T> remainder(final double a) {
347         return new FieldGradient<>(FastMath.IEEEremainder(value, a), grad);
348     }
349 
350     /** {@inheritDoc} */
351     @Override
352     public FieldGradient<T> remainder(final FieldGradient<T> a) {
353 
354         // compute k such that lhs % rhs = lhs - k rhs
355         final T rem = FastMath.IEEEremainder(value, a.value);
356         final T k   = FastMath.rint(value.subtract(rem).divide(a.value));
357 
358         final FieldGradient<T> result = newInstance(rem);
359         for (int i = 0; i < grad.length; ++i) {
360             result.grad[i] = grad[i].subtract(k.multiply(a.grad[i]));
361         }
362         return result;
363 
364     }
365 
366     /** {@inheritDoc} */
367     @Override
368     public FieldGradient<T> negate() {
369         final FieldGradient<T> result = newInstance(value.negate());
370         for (int i = 0; i < grad.length; ++i) {
371             result.grad[i] = grad[i].negate();
372         }
373         return result;
374     }
375 
376     /** {@inheritDoc} */
377     @Override
378     public FieldGradient<T> abs() {
379         if (Double.doubleToLongBits(value.getReal()) < 0) {
380             // we use the bits representation to also handle -0.0
381             return negate();
382         } else {
383             return this;
384         }
385     }
386 
387     /**
388      * Returns the instance with the sign of the argument.
389      * A NaN {@code sign} argument is treated as positive.
390      *
391      * @param sign the sign for the returned value
392      * @return the instance with the same sign as the {@code sign} argument
393      */
394     public FieldGradient<T> copySign(final T sign) {
395         long m = Double.doubleToLongBits(value.getReal());
396         long s = Double.doubleToLongBits(sign.getReal());
397         if ((m >= 0 && s >= 0) || (m < 0 && s < 0)) { // Sign is currently OK
398             return this;
399         }
400         return negate(); // flip sign
401     }
402 
403     /** {@inheritDoc} */
404     @Override
405     public FieldGradient<T> copySign(final FieldGradient<T> sign) {
406         long m = Double.doubleToLongBits(value.getReal());
407         long s = Double.doubleToLongBits(sign.value.getReal());
408         if ((m >= 0 && s >= 0) || (m < 0 && s < 0)) { // Sign is currently OK
409             return this;
410         }
411         return negate(); // flip sign
412     }
413 
414     /** {@inheritDoc} */
415     @Override
416     public FieldGradient<T> copySign(final double sign) {
417         long m = Double.doubleToLongBits(value.getReal());
418         long s = Double.doubleToLongBits(sign);
419         if ((m >= 0 && s >= 0) || (m < 0 && s < 0)) { // Sign is currently OK
420             return this;
421         }
422         return negate(); // flip sign
423     }
424 
425     /** {@inheritDoc} */
426     @Override
427     public FieldGradient<T> scalb(final int n) {
428         final FieldGradient<T> result = newInstance(FastMath.scalb(value, n));
429         for (int i = 0; i < grad.length; ++i) {
430             result.grad[i] = FastMath.scalb(grad[i], n);
431         }
432         return result;
433     }
434 
435     /** {@inheritDoc} */
436     @Override
437     public FieldGradient<T> hypot(final FieldGradient<T> y) {
438 
439         if (Double.isInfinite(value.getReal()) || Double.isInfinite(y.value.getReal())) {
440             return newInstance(Double.POSITIVE_INFINITY);
441         } else if (Double.isNaN(value.getReal()) || Double.isNaN(y.value.getReal())) {
442             return newInstance(Double.NaN);
443         } else {
444 
445             final int expX = getExponent();
446             final int expY = y.getExponent();
447             if (expX > expY + 27) {
448                 // y is negligible with respect to x
449                 return abs();
450             } else if (expY > expX + 27) {
451                 // x is negligible with respect to y
452                 return y.abs();
453             } else {
454 
455                 // find an intermediate scale to avoid both overflow and underflow
456                 final int middleExp = (expX + expY) / 2;
457 
458                 // scale parameters without losing precision
459                 final FieldGradient<T> scaledX = scalb(-middleExp);
460                 final FieldGradient<T> scaledY = y.scalb(-middleExp);
461 
462                 // compute scaled hypotenuse
463                 final FieldGradient<T> scaledH =
464                         scaledX.multiply(scaledX).add(scaledY.multiply(scaledY)).sqrt();
465 
466                 // remove scaling
467                 return scaledH.scalb(middleExp);
468 
469             }
470 
471         }
472     }
473 
474     /** Compute composition of the instance by a function.
475      * @param g0 value of the function at the current point (i.e. at {@code g(getValue())})
476      * @param g1 first derivative of the function at the current point (i.e. at {@code g'(getValue())})
477      * @return g(this)
478      */
479     @Override
480     public FieldGradient<T> compose(final T g0, final T g1) {
481         final FieldGradient<T> result = newInstance(g0);
482         for (int i = 0; i < grad.length; ++i) {
483             result.grad[i] = g1.multiply(grad[i]);
484         }
485         return result;
486     }
487 
488     /** {@inheritDoc} */
489     @Override
490     public FieldGradient<T> rootN(final int n) {
491         if (n == 2) {
492             return sqrt();
493         } else if (n == 3) {
494             return cbrt();
495         } else {
496             final T r = FastMath.pow(value, 1.0 / n);
497             return compose(r, FastMath.pow(r, n - 1).multiply(n).reciprocal());
498         }
499     }
500 
501     /** {@inheritDoc} */
502     @Override
503     public FieldGradientField<T> getField() {
504         return FieldGradientField.getField(getValueField(), getFreeParameters());
505     }
506 
507     /** Compute a<sup>x</sup> where a is a double and x a {@link FieldGradient}
508      * @param a number to exponentiate
509      * @param x power to apply
510      * @param <T> the type of the function parameters and value
511      * @return a<sup>x</sup>
512      */
513     public static <T extends CalculusFieldElement<T>> FieldGradient<T> pow(final double a, final FieldGradient<T> x) {
514         if (a == 0) {
515             return x.getField().getZero();
516         } else {
517             final T aX    = FastMath.pow(x.value.newInstance(a), x.value);
518             final T aXlnA = aX.multiply(FastMath.log(a));
519             final FieldGradient<T> result = x.newInstance(aX);
520             for (int i = 0; i < x.grad.length; ++i) {
521                 result.grad[i] =  aXlnA.multiply(x.grad[i]);
522             }
523             return result;
524         }
525     }
526 
527     /** {@inheritDoc} */
528     @Override
529     public FieldGradient<T> pow(final double p) {
530         if (p == 0) {
531             return getField().getOne();
532         } else {
533             final T f0Pm1 = FastMath.pow(value, p - 1);
534             return compose(f0Pm1.multiply(value), f0Pm1.multiply(p));
535         }
536     }
537 
538     /** {@inheritDoc} */
539     @Override
540     public FieldGradient<T> pow(final int n) {
541         if (n == 0) {
542             return getField().getOne();
543         } else {
544             final T f0Nm1 = FastMath.pow(value, n - 1);
545             return compose(f0Nm1.multiply(value), f0Nm1.multiply(n));
546         }
547     }
548 
549     /** {@inheritDoc} */
550     @Override
551     public FieldSinCos<FieldGradient<T>> sinCos() {
552         final FieldSinCos<T> sinCos = FastMath.sinCos(value);
553         final FieldGradient<T> sin = newInstance(sinCos.sin());
554         final FieldGradient<T> cos = newInstance(sinCos.cos());
555         final T mSin = sinCos.sin().negate();
556         for (int i = 0; i < grad.length; ++i) {
557             sin.grad[i] =  grad[i].multiply(sinCos.cos());
558             cos.grad[i] =  grad[i].multiply(mSin);
559         }
560         return new FieldSinCos<>(sin, cos);
561     }
562 
563     /** {@inheritDoc} */
564     @Override
565     public FieldGradient<T> atan2(final FieldGradient<T> x) {
566         final T inv = value.square().add(x.value.multiply(x.value)).reciprocal();
567         final FieldGradient<T> result = newInstance(FastMath.atan2(value, x.value));
568         final T xValueInv = x.value.multiply(inv);
569         final T mValueInv = value.negate().multiply(inv);
570         for (int i = 0; i < grad.length; ++i) {
571             result.grad[i] = xValueInv.multiply(grad[i]).add(x.grad[i].multiply(mValueInv));
572         }
573         return result;
574     }
575 
576     /** {@inheritDoc} */
577     @Override
578     public FieldSinhCosh<FieldGradient<T>> sinhCosh() {
579         final FieldSinhCosh<T> sinhCosh = FastMath.sinhCosh(value);
580         final FieldGradient<T> sinh = newInstance(sinhCosh.sinh());
581         final FieldGradient<T> cosh = newInstance(sinhCosh.cosh());
582         for (int i = 0; i < grad.length; ++i) {
583             sinh.grad[i] = grad[i].multiply(sinhCosh.cosh());
584             cosh.grad[i] = grad[i].multiply(sinhCosh.sinh());
585         }
586         return new FieldSinhCosh<>(sinh, cosh);
587     }
588 
589     /** {@inheritDoc} */
590     @Override
591     public FieldGradient<T> toDegrees() {
592         final FieldGradient<T> result = newInstance(FastMath.toDegrees(value));
593         for (int i = 0; i < grad.length; ++i) {
594             result.grad[i] = FastMath.toDegrees(grad[i]);
595         }
596         return result;
597     }
598 
599     /** {@inheritDoc} */
600     @Override
601     public FieldGradient<T> toRadians() {
602         final FieldGradient<T> result = newInstance(FastMath.toRadians(value));
603         for (int i = 0; i < grad.length; ++i) {
604             result.grad[i] = FastMath.toRadians(grad[i]);
605         }
606         return result;
607     }
608 
609     /** Evaluate Taylor expansion of a gradient.
610      * @param delta parameters offsets (&Delta;x, &Delta;y, ...)
611      * @return value of the Taylor expansion at x + &Delta;x, y + &Delta;y, ...
612      */
613     public T taylor(final double... delta) {
614         T result = value;
615         for (int i = 0; i < grad.length; ++i) {
616             result = result.add(grad[i].multiply(delta[i]));
617         }
618         return result;
619     }
620 
621     /** Evaluate Taylor expansion of a gradient.
622      * @param delta parameters offsets (&Delta;x, &Delta;y, ...)
623      * @return value of the Taylor expansion at x + &Delta;x, y + &Delta;y, ...
624      */
625     public T taylor(@SuppressWarnings("unchecked") final T... delta) {
626         T result = value;
627         for (int i = 0; i < grad.length; ++i) {
628             result = result.add(grad[i].multiply(delta[i]));
629         }
630         return result;
631     }
632 
633     /** {@inheritDoc} */
634     @Override
635     public FieldGradient<T> linearCombination(final FieldGradient<T>[] a, final FieldGradient<T>[] b) {
636 
637         // extract values and first derivatives
638         final Field<T> field = a[0].value.getField();
639         final int n  = a.length;
640         final T[] a0 = MathArrays.buildArray(field, n);
641         final T[] b0 = MathArrays.buildArray(field, n);
642         final T[] a1 = MathArrays.buildArray(field, 2 * n);
643         final T[] b1 = MathArrays.buildArray(field, 2 * n);
644         for (int i = 0; i < n; ++i) {
645             final FieldGradient<T> ai = a[i];
646             final FieldGradient<T> bi = b[i];
647             a0[i]         = ai.value;
648             b0[i]         = bi.value;
649             a1[2 * i]     = ai.value;
650             b1[2 * i + 1] = bi.value;
651         }
652 
653         final FieldGradient<T> result = newInstance(a[0].value.linearCombination(a0, b0));
654         for (int k = 0; k < grad.length; ++k) {
655             for (int i = 0; i < n; ++i) {
656                 a1[2 * i + 1] = a[i].grad[k];
657                 b1[2 * i]     = b[i].grad[k];
658             }
659             result.grad[k] = a[0].value.linearCombination(a1, b1);
660         }
661         return result;
662 
663     }
664 
665     /**
666      * Compute a linear combination.
667      * @param a Factors.
668      * @param b Factors.
669      * @return <code>&Sigma;<sub>i</sub> a<sub>i</sub> b<sub>i</sub></code>.
670      * @throws MathIllegalArgumentException if arrays dimensions don't match
671      */
672     public FieldGradient<T> linearCombination(final T[] a, final FieldGradient<T>[] b) {
673 
674         // extract values and first derivatives
675         final Field<T> field = b[0].value.getField();
676         final int      n  = b.length;
677         final T[] b0 = MathArrays.buildArray(field, n);
678         final T[] b1 = MathArrays.buildArray(field, n);
679         for (int i = 0; i < n; ++i) {
680             b0[i] = b[i].value;
681         }
682 
683         final FieldGradient<T> result = newInstance(b[0].value.linearCombination(a, b0));
684         for (int k = 0; k < grad.length; ++k) {
685             for (int i = 0; i < n; ++i) {
686                 b1[i] = b[i].grad[k];
687             }
688             result.grad[k] = b[0].value.linearCombination(a, b1);
689         }
690         return result;
691 
692     }
693 
694     /** {@inheritDoc} */
695     @Override
696     public FieldGradient<T> linearCombination(final double[] a, final FieldGradient<T>[] b) {
697 
698         // extract values and first derivatives
699         final Field<T> field = b[0].value.getField();
700         final int      n  = b.length;
701         final T[] b0 = MathArrays.buildArray(field, n);
702         final T[] b1 = MathArrays.buildArray(field, n);
703         for (int i = 0; i < n; ++i) {
704             b0[i] = b[i].value;
705         }
706 
707         final FieldGradient<T> result = newInstance(b[0].value.linearCombination(a, b0));
708         for (int k = 0; k < grad.length; ++k) {
709             for (int i = 0; i < n; ++i) {
710                 b1[i] = b[i].grad[k];
711             }
712             result.grad[k] = b[0].value.linearCombination(a, b1);
713         }
714         return result;
715 
716     }
717 
718     /** {@inheritDoc} */
719     @Override
720     public FieldGradient<T> linearCombination(final FieldGradient<T> a1, final FieldGradient<T> b1,
721                                               final FieldGradient<T> a2, final FieldGradient<T> b2) {
722         final FieldGradient<T> result = newInstance(a1.value.linearCombination(a1.value, b1.value,
723                                                                                a2.value, b2.value));
724         for (int i = 0; i < b1.grad.length; ++i) {
725             result.grad[i] = a1.value.linearCombination(a1.value,       b1.grad[i],
726                                                             a1.grad[i], b1.value,
727                                                             a2.value,       b2.grad[i],
728                                                             a2.grad[i], b2.value);
729         }
730         return result;
731     }
732 
733     /** {@inheritDoc} */
734     @Override
735     public FieldGradient<T> linearCombination(final double a1, final FieldGradient<T> b1,
736                                               final double a2, final FieldGradient<T> b2) {
737         final FieldGradient<T> result = newInstance(b1.value.linearCombination(a1, b1.value,
738                                                                                a2, b2.value));
739         for (int i = 0; i < b1.grad.length; ++i) {
740             result.grad[i] = b1.value.linearCombination(a1, b1.grad[i],
741                                                             a2, b2.grad[i]);
742         }
743         return result;
744     }
745 
746     /** {@inheritDoc} */
747     @Override
748     public FieldGradient<T> linearCombination(final FieldGradient<T> a1, final FieldGradient<T> b1,
749                                               final FieldGradient<T> a2, final FieldGradient<T> b2,
750                                               final FieldGradient<T> a3, final FieldGradient<T> b3) {
751         final Field<T> field = a1.value.getField();
752         final T[] a = MathArrays.buildArray(field, 6);
753         final T[] b = MathArrays.buildArray(field, 6);
754         a[0] = a1.value;
755         a[2] = a2.value;
756         a[4] = a3.value;
757         b[1] = b1.value;
758         b[3] = b2.value;
759         b[5] = b3.value;
760         final FieldGradient<T> result = newInstance(a1.value.linearCombination(a1.value, b1.value,
761                                                                                a2.value, b2.value,
762                                                                                a3.value, b3.value));
763         for (int i = 0; i < b1.grad.length; ++i) {
764             a[1] = a1.grad[i];
765             a[3] = a2.grad[i];
766             a[5] = a3.grad[i];
767             b[0] = b1.grad[i];
768             b[2] = b2.grad[i];
769             b[4] = b3.grad[i];
770             result.grad[i] = a1.value.linearCombination(a, b);
771         }
772         return result;
773     }
774 
775     /**
776      * Compute a linear combination.
777      * @param a1 first factor of the first term
778      * @param b1 second factor of the first term
779      * @param a2 first factor of the second term
780      * @param b2 second factor of the second term
781      * @param a3 first factor of the third term
782      * @param b3 second factor of the third term
783      * @return a<sub>1</sub>&times;b<sub>1</sub> +
784      * a<sub>2</sub>&times;b<sub>2</sub> + a<sub>3</sub>&times;b<sub>3</sub>
785      * @see #linearCombination(double, FieldGradient, double, FieldGradient)
786      * @see #linearCombination(double, FieldGradient, double, FieldGradient, double, FieldGradient, double, FieldGradient)
787      * @exception MathIllegalArgumentException if number of free parameters or orders are inconsistent
788      */
789     public FieldGradient<T> linearCombination(final T a1, final FieldGradient<T> b1,
790                                               final T a2, final FieldGradient<T> b2,
791                                               final T a3, final FieldGradient<T> b3) {
792         final FieldGradient<T> result = newInstance(b1.value.linearCombination(a1, b1.value,
793                                                                                a2, b2.value,
794                                                                                a3, b3.value));
795         for (int i = 0; i < b1.grad.length; ++i) {
796             result.grad[i] = b1.value.linearCombination(a1, b1.grad[i],
797                                                         a2, b2.grad[i],
798                                                         a3, b3.grad[i]);
799         }
800         return result;
801     }
802 
803     /** {@inheritDoc} */
804     @Override
805     public FieldGradient<T> linearCombination(final double a1, final FieldGradient<T> b1,
806                                               final double a2, final FieldGradient<T> b2,
807                                               final double a3, final FieldGradient<T> b3) {
808         final FieldGradient<T> result = newInstance(b1.value.linearCombination(a1, b1.value,
809                                                                                a2, b2.value,
810                                                                                a3, b3.value));
811         for (int i = 0; i < b1.grad.length; ++i) {
812             result.grad[i] = b1.value.linearCombination(a1, b1.grad[i],
813                                                             a2, b2.grad[i],
814                                                             a3, b3.grad[i]);
815         }
816         return result;
817     }
818 
819     /** {@inheritDoc} */
820     @Override
821     public FieldGradient<T> linearCombination(final FieldGradient<T> a1, final FieldGradient<T> b1,
822                                               final FieldGradient<T> a2, final FieldGradient<T> b2,
823                                               final FieldGradient<T> a3, final FieldGradient<T> b3,
824                                               final FieldGradient<T> a4, final FieldGradient<T> b4) {
825         final Field<T> field = a1.value.getField();
826         final T[] a = MathArrays.buildArray(field, 8);
827         final T[] b = MathArrays.buildArray(field, 8);
828         a[0] = a1.value;
829         a[2] = a2.value;
830         a[4] = a3.value;
831         a[6] = a4.value;
832         b[1] = b1.value;
833         b[3] = b2.value;
834         b[5] = b3.value;
835         b[7] = b4.value;
836         final FieldGradient<T> result = newInstance(a1.value.linearCombination(a1.value, b1.value,
837                                                                                a2.value, b2.value,
838                                                                                a3.value, b3.value,
839                                                                                a4.value, b4.value));
840         for (int i = 0; i < b1.grad.length; ++i) {
841             a[1] = a1.grad[i];
842             a[3] = a2.grad[i];
843             a[5] = a3.grad[i];
844             a[7] = a4.grad[i];
845             b[0] = b1.grad[i];
846             b[2] = b2.grad[i];
847             b[4] = b3.grad[i];
848             b[6] = b4.grad[i];
849             result.grad[i] = a1.value.linearCombination(a, b);
850         }
851         return result;
852     }
853 
854     /** {@inheritDoc} */
855     @Override
856     public FieldGradient<T> linearCombination(final double a1, final FieldGradient<T> b1,
857                                               final double a2, final FieldGradient<T> b2,
858                                               final double a3, final FieldGradient<T> b3,
859                                               final double a4, final FieldGradient<T> b4) {
860         final FieldGradient<T> result = newInstance(b1.value.linearCombination(a1, b1.value,
861                                                                                a2, b2.value,
862                                                                                a3, b3.value,
863                                                                                a4, b4.value));
864         for (int i = 0; i < b1.grad.length; ++i) {
865             result.grad[i] = b1.value.linearCombination(a1, b1.grad[i],
866                                                             a2, b2.grad[i],
867                                                             a3, b3.grad[i],
868                                                             a4, b4.grad[i]);
869         }
870         return result;
871     }
872 
873     /** {@inheritDoc} */
874     @Override
875     public FieldGradient<T> getPi() {
876         return new FieldGradient<>(getValueField().getZero().getPi(), getFreeParameters());
877     }
878 
879     /** Test for the equality of two univariate derivatives.
880      * <p>
881      * univariate derivatives are considered equal if they have the same derivatives.
882      * </p>
883      * @param other Object to test for equality to this
884      * @return true if two univariate derivatives are equal
885      */
886     @Override
887     public boolean equals(Object other) {
888 
889         if (this == other) {
890             return true;
891         }
892 
893         if (other instanceof FieldGradient) {
894             @SuppressWarnings("unchecked")
895             final FieldGradient<T> rhs = (FieldGradient<T>) other;
896             if (!value.equals(rhs.value) || grad.length != rhs.grad.length) {
897                 return false;
898             }
899             for (int i = 0; i < grad.length; ++i) {
900                 if (!grad[i].equals(rhs.grad[i])) {
901                     return false;
902                 }
903             }
904             return true;
905         }
906 
907         return false;
908 
909     }
910 
911     /** Get a hashCode for the univariate derivative.
912      * @return a hash code value for this object
913      */
914     @Override
915     public int hashCode() {
916         return 129 + 7 *value.hashCode() - 15 * Arrays.hashCode(grad);
917     }
918 
919 }