Gradient.java

  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. import java.io.Serializable;
  19. import java.util.Arrays;

  20. import org.hipparchus.exception.LocalizedCoreFormats;
  21. import org.hipparchus.exception.MathIllegalArgumentException;
  22. import org.hipparchus.util.FastMath;
  23. import org.hipparchus.util.FieldSinCos;
  24. import org.hipparchus.util.FieldSinhCosh;
  25. import org.hipparchus.util.MathArrays;
  26. import org.hipparchus.util.MathUtils;
  27. import org.hipparchus.util.SinCos;
  28. import org.hipparchus.util.SinhCosh;

  29. /** Class representing both the value and the differentials of a function.
  30.  * <p>This class is a stripped-down version of {@link DerivativeStructure}
  31.  * with {@link DerivativeStructure#getOrder() derivation order} limited to one.
  32.  * It should have less overhead than {@link DerivativeStructure} in its domain.</p>
  33.  * <p>This class is an implementation of Rall's numbers. Rall's numbers are an
  34.  * extension to the real numbers used throughout mathematical expressions; they hold
  35.  * the derivative together with the value of a function.</p>
  36.  * <p>{@link Gradient} instances can be used directly thanks to
  37.  * the arithmetic operators to the mathematical functions provided as
  38.  * methods by this class (+, -, *, /, %, sin, cos ...).</p>
  39.  * <p>Implementing complex expressions by hand using {@link Derivative}-based
  40.  * classes (or in fact any {@link org.hipparchus.CalculusFieldElement} class) is
  41.  * a tedious and error-prone task but has the advantage of not requiring users
  42.  * to compute the derivatives by themselves and allowing to switch for one
  43.  * derivative implementation to another as they all share the same filed API.</p>
  44.  * <p>Instances of this class are guaranteed to be immutable.</p>
  45.  * @see DerivativeStructure
  46.  * @see UnivariateDerivative1
  47.  * @see UnivariateDerivative2
  48.  * @see FieldDerivativeStructure
  49.  * @see FieldUnivariateDerivative1
  50.  * @see FieldUnivariateDerivative2
  51.  * @see FieldGradient
  52.  * @since 1.7
  53.  */
  54. public class Gradient implements Derivative1<Gradient>, Serializable {

  55.     /** Serializable UID. */
  56.     private static final long serialVersionUID = 20200520L;

  57.     /** Value of the function. */
  58.     private final double value;

  59.     /** Gradient of the function. */
  60.     private final double[] grad;

  61.     /** Build an instance with values and uninitialized derivatives array.
  62.      * @param value value of the function
  63.      * @param freeParameters number of free parameters
  64.      */
  65.     private Gradient(final double value, int freeParameters) {
  66.         this.value = value;
  67.         this.grad  = new double[freeParameters];
  68.     }

  69.     /** Build an instance with value and derivatives array, used for performance internally (no copy).
  70.      * @param value value of the function
  71.      * @param gradient derivatives
  72.      * @since 3.1
  73.      */
  74.     private Gradient(final double[] gradient, final double value) {
  75.         this.value = value;
  76.         this.grad  = gradient;
  77.     }

  78.     /** Build an instance with values and derivative.
  79.      * @param value value of the function
  80.      * @param gradient gradient of the function
  81.      */
  82.     public Gradient(final double value, final double... gradient) {
  83.         this(value, gradient.length);
  84.         System.arraycopy(gradient, 0, grad, 0, grad.length);
  85.     }

  86.     /** Build an instance from a {@link DerivativeStructure}.
  87.      * @param ds derivative structure
  88.      * @exception MathIllegalArgumentException if {@code ds} order
  89.      * is not 1
  90.      */
  91.     public Gradient(final DerivativeStructure ds) throws MathIllegalArgumentException {
  92.         this(ds.getValue(), ds.getFreeParameters());
  93.         MathUtils.checkDimension(ds.getOrder(), 1);
  94.         System.arraycopy(ds.getAllDerivatives(), 1, grad, 0, grad.length);
  95.     }

  96.     /** Build an instance corresponding to a constant value.
  97.      * @param freeParameters number of free parameters (i.e. dimension of the gradient)
  98.      * @param value constant value of the function
  99.      * @return a {@code Gradient} with a constant value and all derivatives set to 0.0
  100.      */
  101.     public static Gradient constant(final int freeParameters, final double value) {
  102.         return new Gradient(value, freeParameters);
  103.     }

  104.     /** Build a {@code Gradient} representing a variable.
  105.      * <p>Instances built using this method are considered
  106.      * to be the free variables with respect to which differentials
  107.      * are computed. As such, their differential with respect to
  108.      * themselves is +1.</p>
  109.      * @param freeParameters number of free parameters (i.e. dimension of the gradient)
  110.      * @param index index of the variable (from 0 to {@link #getFreeParameters() getFreeParameters()} - 1)
  111.      * @param value value of the variable
  112.      * @return a {@code Gradient} with a constant value and all derivatives set to 0.0 except the
  113.      * one at {@code index} which will be set to 1.0
  114.      */
  115.     public static Gradient variable(final int freeParameters, final int index, final double value) {
  116.         final Gradient g = new Gradient(value, freeParameters);
  117.         g.grad[index] = 1.0;
  118.         return g;
  119.     }

  120.     /** {@inheritDoc} */
  121.     @Override
  122.     public Gradient newInstance(final double c) {
  123.         return new Gradient(c, new double[grad.length]);
  124.     }

  125.     /** {@inheritDoc} */
  126.     @Override
  127.     public Gradient withValue(final double v) {
  128.         return new Gradient(v, grad);
  129.     }

  130.     /** {@inheritDoc} */
  131.     @Override
  132.     public Gradient getAddendum() {
  133.         return new Gradient(0, grad);
  134.     }

  135.     /** Get the value part of the function.
  136.      * @return value part of the value of the function
  137.      */
  138.     @Override
  139.     public double getValue() {
  140.         return value;
  141.     }

  142.     /** Get the gradient part of the function.
  143.      * @return gradient part of the value of the function
  144.      * @see #getPartialDerivative(int)
  145.      */
  146.     public double[] getGradient() {
  147.         return grad.clone();
  148.     }

  149.     /** {@inheritDoc} */
  150.     @Override
  151.     public int getFreeParameters() {
  152.         return grad.length;
  153.     }

  154.     /** {@inheritDoc} */
  155.     @Override
  156.     public double getPartialDerivative(final int ... orders)
  157.             throws MathIllegalArgumentException {

  158.         // check the number of components
  159.         if (orders.length != grad.length) {
  160.             throw new MathIllegalArgumentException(LocalizedCoreFormats.DIMENSIONS_MISMATCH,
  161.                     orders.length, grad.length);
  162.         }

  163.         // check that either all derivation orders are set to 0,
  164.         // or that only one is set to 1 and all other ones are set to 0
  165.         int selected = -1;
  166.         for (int i = 0; i < orders.length; ++i) {
  167.             if (orders[i] != 0) {
  168.                 if (selected >= 0 || orders[i] != 1) {
  169.                     throw new MathIllegalArgumentException(LocalizedCoreFormats.DERIVATION_ORDER_NOT_ALLOWED,
  170.                             orders[i]);
  171.                 }
  172.                 // found the component set to derivation order 1
  173.                 selected = i;
  174.             }
  175.         }

  176.         return (selected < 0) ? value : grad[selected];

  177.     }

  178.     /** Get the partial derivative with respect to one parameter.
  179.      * @param n index of the parameter (counting from 0)
  180.      * @return partial derivative with respect to the n<sup>th</sup> parameter
  181.      * @exception MathIllegalArgumentException if n is either negative or larger
  182.      * or equal to {@link #getFreeParameters()}
  183.      */
  184.     public double getPartialDerivative(final int n) throws MathIllegalArgumentException {
  185.         if (n < 0 || n >= grad.length) {
  186.             throw new MathIllegalArgumentException(LocalizedCoreFormats.OUT_OF_RANGE_SIMPLE, n, 0, grad.length - 1);
  187.         }
  188.         return grad[n];
  189.     }

  190.     /** Convert the instance to a {@link DerivativeStructure}.
  191.      * @return derivative structure with same value and derivative as the instance
  192.      */
  193.     public DerivativeStructure toDerivativeStructure() {
  194.         final double[] derivatives = new double[1 + grad.length];
  195.         derivatives[0] = value;
  196.         System.arraycopy(grad, 0, derivatives, 1, grad.length);
  197.         return getField().getConversionFactory().build(derivatives);
  198.     }

  199.     /** {@inheritDoc} */
  200.     @Override
  201.     public Gradient add(final Gradient a) {
  202.         final double[] gradient = new double[grad.length];
  203.         for (int i = 0; i < grad.length; ++i) {
  204.             gradient[i] = grad[i] + a.grad[i];
  205.         }
  206.         return new Gradient(gradient, value + a.value);
  207.     }

  208.     /** {@inheritDoc} */
  209.     @Override
  210.     public Gradient subtract(final Gradient a) {
  211.         final double[] gradient = new double[grad.length];
  212.         for (int i = 0; i < grad.length; ++i) {
  213.             gradient[i] = grad[i] - a.grad[i];
  214.         }
  215.         return new Gradient(gradient, value - a.value);
  216.     }

  217.     /** {@inheritDoc} */
  218.     @Override
  219.     public Gradient multiply(final int n) {
  220.         final double[] gradient = new double[grad.length];
  221.         for (int i = 0; i < grad.length; ++i) {
  222.             gradient[i] = grad[i] * n;
  223.         }
  224.         return new Gradient(gradient, value * n);
  225.     }

  226.     /** {@inheritDoc} */
  227.     @Override
  228.     public Gradient multiply(final double a) {
  229.         final double[] gradient = new double[grad.length];
  230.         for (int i = 0; i < grad.length; ++i) {
  231.             gradient[i] = grad[i] * a;
  232.         }
  233.         return new Gradient(gradient, value * a);
  234.     }

  235.     /** {@inheritDoc} */
  236.     @Override
  237.     public Gradient multiply(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.value + value * a.grad[i];
  241.         }
  242.         return new Gradient(gradient, value * a.value);
  243.     }

  244.     /** {@inheritDoc} */
  245.     @Override
  246.     public Gradient divide(final double a) {
  247.         final double[] gradient = new double[grad.length];
  248.         for (int i = 0; i < grad.length; ++i) {
  249.             gradient[i] = grad[i] / a;
  250.         }
  251.         return new Gradient(gradient, value / a);
  252.     }

  253.     /** {@inheritDoc} */
  254.     @Override
  255.     public Gradient divide(final Gradient a) {
  256.         final double inv1 = 1.0 / a.value;
  257.         final double inv2 = inv1 * inv1;
  258.         final double[] gradient = new double[grad.length];
  259.         for (int i = 0; i < grad.length; ++i) {
  260.             gradient[i] = (grad[i] * a.value - value * a.grad[i]) * inv2;
  261.         }
  262.         return new Gradient(gradient, value * inv1);
  263.     }

  264.     /** {@inheritDoc} */
  265.     @Override
  266.     public Gradient remainder(final Gradient a) {

  267.         // compute k such that lhs % rhs = lhs - k rhs
  268.         final double rem = FastMath.IEEEremainder(value, a.value);
  269.         final double k   = FastMath.rint((value - rem) / a.value);

  270.         final double[] gradient = new double[grad.length];
  271.         for (int i = 0; i < grad.length; ++i) {
  272.             gradient[i] = grad[i] - k * a.grad[i];
  273.         }
  274.         return new Gradient(gradient, rem);

  275.     }

  276.     /** {@inheritDoc} */
  277.     @Override
  278.     public Gradient negate() {
  279.         final double[] gradient = new double[grad.length];
  280.         for (int i = 0; i < grad.length; ++i) {
  281.             gradient[i] = -grad[i];
  282.         }
  283.         return new Gradient(gradient, -value);
  284.     }

  285.     /** {@inheritDoc} */
  286.     @Override
  287.     public Gradient abs() {
  288.         if (Double.doubleToLongBits(value) < 0) {
  289.             // we use the bits representation to also handle -0.0
  290.             return negate();
  291.         } else {
  292.             return this;
  293.         }
  294.     }

  295.     /** {@inheritDoc} */
  296.     @Override
  297.     public Gradient copySign(final Gradient sign) {
  298.         long m = Double.doubleToLongBits(value);
  299.         long s = Double.doubleToLongBits(sign.value);
  300.         if ((m >= 0 && s >= 0) || (m < 0 && s < 0)) { // Sign is currently OK
  301.             return this;
  302.         }
  303.         return negate(); // flip sign
  304.     }

  305.     /** {@inheritDoc} */
  306.     @Override
  307.     public Gradient copySign(final double sign) {
  308.         long m = Double.doubleToLongBits(value);
  309.         long s = Double.doubleToLongBits(sign);
  310.         if ((m >= 0 && s >= 0) || (m < 0 && s < 0)) { // Sign is currently OK
  311.             return this;
  312.         }
  313.         return negate(); // flip sign
  314.     }

  315.     /** {@inheritDoc} */
  316.     @Override
  317.     public Gradient scalb(final int n) {
  318.         final double[] gradient = new double[grad.length];
  319.         for (int i = 0; i < grad.length; ++i) {
  320.             gradient[i] = FastMath.scalb(grad[i], n);
  321.         }
  322.         return new Gradient(gradient, FastMath.scalb(value, n));
  323.     }

  324.     /** {@inheritDoc} */
  325.     @Override
  326.     public Gradient hypot(final Gradient y) {

  327.         if (Double.isInfinite(value) || Double.isInfinite(y.value)) {
  328.             return newInstance(Double.POSITIVE_INFINITY);
  329.         } else if (Double.isNaN(value) || Double.isNaN(y.value)) {
  330.             return newInstance(Double.NaN);
  331.         } else {

  332.             final int expX = getExponent();
  333.             final int expY = y.getExponent();
  334.             if (expX > expY + 27) {
  335.                 // y is negligible with respect to x
  336.                 return abs();
  337.             } else if (expY > expX + 27) {
  338.                 // x is negligible with respect to y
  339.                 return y.abs();
  340.             } else {

  341.                 // find an intermediate scale to avoid both overflow and underflow
  342.                 final int middleExp = (expX + expY) / 2;

  343.                 // scale parameters without losing precision
  344.                 final Gradient scaledX = scalb(-middleExp);
  345.                 final Gradient scaledY = y.scalb(-middleExp);

  346.                 // compute scaled hypotenuse
  347.                 final Gradient scaledH =
  348.                         scaledX.multiply(scaledX).add(scaledY.multiply(scaledY)).sqrt();

  349.                 // remove scaling
  350.                 return scaledH.scalb(middleExp);

  351.             }

  352.         }
  353.     }

  354.     /** {@inheritDoc} */
  355.     @Override
  356.     public Gradient compose(final double... f) {
  357.         MathUtils.checkDimension(f.length, getOrder() + 1);
  358.         return compose(f[0], f[1]);
  359.     }

  360.     /** {@inheritDoc} */
  361.     @Override
  362.     public Gradient compose(final double f0, final double f1) {
  363.         final double[] gradient = new double[grad.length];
  364.         for (int i = 0; i < grad.length; ++i) {
  365.             gradient[i] = f1 * grad[i];
  366.         }
  367.         return new Gradient(gradient, f0);
  368.     }

  369.     /** {@inheritDoc} */
  370.     @Override
  371.     public GradientField getField() {
  372.         return GradientField.getField(getFreeParameters());
  373.     }

  374.     /** Compute a<sup>x</sup> where a is a double and x a {@link Gradient}
  375.      * @param a number to exponentiate
  376.      * @param x power to apply
  377.      * @return a<sup>x</sup>
  378.      */
  379.     public static Gradient pow(final double a, final Gradient x) {
  380.         if (a == 0) {
  381.             return x.getField().getZero();
  382.         } else {
  383.             final double aX = FastMath.pow(a, x.value);
  384.             final double aXlnA = aX * FastMath.log(a);
  385.             final double[] gradient = new double[x.getFreeParameters()];
  386.             for (int i = 0; i < gradient.length; ++i) {
  387.                 gradient[i] =  aXlnA * x.grad[i];
  388.             }
  389.             return new Gradient(gradient, aX);
  390.         }
  391.     }

  392.     /** {@inheritDoc} */
  393.     @Override
  394.     public Gradient pow(final double p) {
  395.         if (p == 0) {
  396.             return getField().getOne();
  397.         } else {
  398.             final double valuePm1 = FastMath.pow(value, p - 1);
  399.             return compose(valuePm1 * value, p * valuePm1);
  400.         }
  401.     }

  402.     /** {@inheritDoc} */
  403.     @Override
  404.     public Gradient pow(final int n) {
  405.         if (n == 0) {
  406.             return getField().getOne();
  407.         } else {
  408.             final double valueNm1 = FastMath.pow(value, n - 1);
  409.             return compose(valueNm1 * value, n * valueNm1);
  410.         }
  411.     }

  412.     /** {@inheritDoc} */
  413.     @Override
  414.     public FieldSinCos<Gradient> sinCos() {
  415.         final SinCos sinCos = FastMath.sinCos(value);
  416.         final double[] gradSin = new double[grad.length];
  417.         final double[] gradCos = new double[grad.length];
  418.         for (int i = 0; i < grad.length; ++i) {
  419.             gradSin[i] =  grad[i] * sinCos.cos();
  420.             gradCos[i] =  -grad[i] * sinCos.sin();
  421.         }
  422.         final Gradient sin = new Gradient(gradSin, sinCos.sin());
  423.         final Gradient cos = new Gradient(gradCos, sinCos.cos());
  424.         return new FieldSinCos<>(sin, cos);
  425.     }

  426.     /** {@inheritDoc} */
  427.     @Override
  428.     public Gradient atan2(final Gradient x) {
  429.         final double inv = 1.0 / (value * value + x.value * x.value);
  430.         final double[] gradient = new double[grad.length];
  431.         for (int i = 0; i < grad.length; ++i) {
  432.             gradient[i] = (x.value * grad[i] - x.grad[i] * value) * inv;
  433.         }
  434.         return new Gradient(gradient, FastMath.atan2(value, x.value));
  435.     }

  436.     /** {@inheritDoc} */
  437.     @Override
  438.     public FieldSinhCosh<Gradient> sinhCosh() {
  439.         final SinhCosh sinhCosh = FastMath.sinhCosh(value);
  440.         final double[] gradSinh = new double[grad.length];
  441.         final double[] gradCosh = new double[grad.length];
  442.         for (int i = 0; i < grad.length; ++i) {
  443.             gradSinh[i] = grad[i] * sinhCosh.cosh();
  444.             gradCosh[i] = grad[i] * sinhCosh.sinh();
  445.         }
  446.         final Gradient sinh = new Gradient(gradSinh, sinhCosh.sinh());
  447.         final Gradient cosh = new Gradient(gradCosh, sinhCosh.cosh());
  448.         return new FieldSinhCosh<>(sinh, cosh);
  449.     }

  450.     /** {@inheritDoc} */
  451.     @Override
  452.     public Gradient toDegrees() {
  453.         final double[] gradient = new double[grad.length];
  454.         for (int i = 0; i < grad.length; ++i) {
  455.             gradient[i] = FastMath.toDegrees(grad[i]);
  456.         }
  457.         return new Gradient(gradient, FastMath.toDegrees(value));
  458.     }

  459.     /** {@inheritDoc} */
  460.     @Override
  461.     public Gradient toRadians() {
  462.         final double[] gradient = new double[grad.length];
  463.         for (int i = 0; i < grad.length; ++i) {
  464.             gradient[i] = FastMath.toRadians(grad[i]);
  465.         }
  466.         return new Gradient(gradient, FastMath.toRadians(value));
  467.     }

  468.     /** Evaluate Taylor expansion a derivative structure.
  469.      * @param delta parameters offsets (&Delta;x, &Delta;y, ...)
  470.      * @return value of the Taylor expansion at x + &Delta;x, y + &Delta;y, ...
  471.      */
  472.     public double taylor(final double... delta) {
  473.         double result = value;
  474.         for (int i = 0; i < grad.length; ++i) {
  475.             result += delta[i] * grad[i];
  476.         }
  477.         return result;
  478.     }

  479.     /** {@inheritDoc} */
  480.     @Override
  481.     public Gradient linearCombination(final Gradient[] a, final Gradient[] b) {

  482.         // extract values and first derivatives
  483.         final int      n  = a.length;
  484.         final double[] a0 = new double[n];
  485.         final double[] b0 = new double[n];
  486.         final double[] a1 = new double[2 * n];
  487.         final double[] b1 = new double[2 * n];
  488.         for (int i = 0; i < n; ++i) {
  489.             final Gradient ai = a[i];
  490.             final Gradient bi = b[i];
  491.             a0[i]         = ai.value;
  492.             b0[i]         = bi.value;
  493.             a1[2 * i]     = ai.value;
  494.             b1[2 * i + 1] = bi.value;
  495.         }

  496.         final Gradient result = newInstance(MathArrays.linearCombination(a0, b0));
  497.         for (int k = 0; k < grad.length; ++k) {
  498.             for (int i = 0; i < n; ++i) {
  499.                 a1[2 * i + 1] = a[i].grad[k];
  500.                 b1[2 * i]     = b[i].grad[k];
  501.             }
  502.             result.grad[k] = MathArrays.linearCombination(a1, b1);
  503.         }
  504.         return result;

  505.     }

  506.     /** {@inheritDoc} */
  507.     @Override
  508.     public Gradient linearCombination(final double[] a, final Gradient[] b) {

  509.         // extract values and first derivatives
  510.         final int      n  = b.length;
  511.         final double[] b0 = new double[n];
  512.         final double[] b1 = new double[n];
  513.         for (int i = 0; i < n; ++i) {
  514.             b0[i] = b[i].value;
  515.         }

  516.         final Gradient result = newInstance(MathArrays.linearCombination(a, b0));
  517.         for (int k = 0; k < grad.length; ++k) {
  518.             for (int i = 0; i < n; ++i) {
  519.                 b1[i] = b[i].grad[k];
  520.             }
  521.             result.grad[k] = MathArrays.linearCombination(a, b1);
  522.         }
  523.         return result;

  524.     }

  525.     /** {@inheritDoc} */
  526.     @Override
  527.     public Gradient linearCombination(final Gradient a1, final Gradient b1,
  528.                                       final Gradient a2, final Gradient b2) {
  529.         final Gradient result = newInstance(MathArrays.linearCombination(a1.value, b1.value,
  530.                 a2.value, b2.value));
  531.         for (int i = 0; i < b1.grad.length; ++i) {
  532.             result.grad[i] = MathArrays.linearCombination(a1.value,       b1.grad[i],
  533.                     a1.grad[i], b1.value,
  534.                     a2.value,       b2.grad[i],
  535.                     a2.grad[i], b2.value);
  536.         }
  537.         return result;
  538.     }

  539.     /** {@inheritDoc} */
  540.     @Override
  541.     public Gradient linearCombination(final double a1, final Gradient b1,
  542.                                       final double a2, final Gradient b2) {
  543.         final Gradient result = newInstance(MathArrays.linearCombination(a1, b1.value,
  544.                 a2, b2.value));
  545.         for (int i = 0; i < b1.grad.length; ++i) {
  546.             result.grad[i] = MathArrays.linearCombination(a1, b1.grad[i],
  547.                     a2, b2.grad[i]);
  548.         }
  549.         return result;
  550.     }

  551.     /** {@inheritDoc} */
  552.     @Override
  553.     public Gradient linearCombination(final Gradient a1, final Gradient b1,
  554.                                       final Gradient a2, final Gradient b2,
  555.                                       final Gradient a3, final Gradient b3) {
  556.         final double[] a = {
  557.                 a1.value, 0, a2.value, 0, a3.value, 0
  558.         };
  559.         final double[] b = {
  560.                 0, b1.value, 0, b2.value, 0, b3.value
  561.         };
  562.         final Gradient result = newInstance(MathArrays.linearCombination(a1.value, b1.value,
  563.                 a2.value, b2.value,
  564.                 a3.value, b3.value));
  565.         for (int i = 0; i < b1.grad.length; ++i) {
  566.             a[1] = a1.grad[i];
  567.             a[3] = a2.grad[i];
  568.             a[5] = a3.grad[i];
  569.             b[0] = b1.grad[i];
  570.             b[2] = b2.grad[i];
  571.             b[4] = b3.grad[i];
  572.             result.grad[i] = MathArrays.linearCombination(a, b);
  573.         }
  574.         return result;
  575.     }

  576.     /** {@inheritDoc} */
  577.     @Override
  578.     public Gradient linearCombination(final double a1, final Gradient b1,
  579.                                       final double a2, final Gradient b2,
  580.                                       final double a3, final Gradient b3) {
  581.         final Gradient result = newInstance(MathArrays.linearCombination(a1, b1.value,
  582.                 a2, b2.value,
  583.                 a3, b3.value));
  584.         for (int i = 0; i < b1.grad.length; ++i) {
  585.             result.grad[i] = MathArrays.linearCombination(a1, b1.grad[i],
  586.                     a2, b2.grad[i],
  587.                     a3, b3.grad[i]);
  588.         }
  589.         return result;
  590.     }

  591.     /** {@inheritDoc} */
  592.     @Override
  593.     public Gradient linearCombination(final Gradient a1, final Gradient b1,
  594.                                       final Gradient a2, final Gradient b2,
  595.                                       final Gradient a3, final Gradient b3,
  596.                                       final Gradient a4, final Gradient b4) {
  597.         final double[] a = {
  598.                 a1.value, 0, a2.value, 0, a3.value, 0, a4.value, 0
  599.         };
  600.         final double[] b = {
  601.                 0, b1.value, 0, b2.value, 0, b3.value, 0, b4.value
  602.         };
  603.         final Gradient result = newInstance(MathArrays.linearCombination(a1.value, b1.value,
  604.                 a2.value, b2.value,
  605.                 a3.value, b3.value,
  606.                 a4.value, b4.value));
  607.         for (int i = 0; i < b1.grad.length; ++i) {
  608.             a[1] = a1.grad[i];
  609.             a[3] = a2.grad[i];
  610.             a[5] = a3.grad[i];
  611.             a[7] = a4.grad[i];
  612.             b[0] = b1.grad[i];
  613.             b[2] = b2.grad[i];
  614.             b[4] = b3.grad[i];
  615.             b[6] = b4.grad[i];
  616.             result.grad[i] = MathArrays.linearCombination(a, b);
  617.         }
  618.         return result;
  619.     }

  620.     /** {@inheritDoc} */
  621.     @Override
  622.     public Gradient linearCombination(final double a1, final Gradient b1,
  623.                                       final double a2, final Gradient b2,
  624.                                       final double a3, final Gradient b3,
  625.                                       final double a4, final Gradient b4) {
  626.         final Gradient result = newInstance(MathArrays.linearCombination(a1, b1.value,
  627.                 a2, b2.value,
  628.                 a3, b3.value,
  629.                 a4, b4.value));
  630.         for (int i = 0; i < b1.grad.length; ++i) {
  631.             result.grad[i] = MathArrays.linearCombination(a1, b1.grad[i],
  632.                     a2, b2.grad[i],
  633.                     a3, b3.grad[i],
  634.                     a4, b4.grad[i]);
  635.         }
  636.         return result;
  637.     }

  638.     /**
  639.      * Add an independent variable to the Taylor expansion.
  640.      * @return object with one more variable
  641.      * @since 4.0
  642.      */
  643.     public Gradient stackVariable() {
  644.         final double[] gradient = new double[this.getFreeParameters() + 1];
  645.         System.arraycopy(this.grad, 0, gradient, 0, this.getFreeParameters());
  646.         return new Gradient(gradient, this.getValue());
  647.     }

  648.     /** Test for the equality of two univariate derivatives.
  649.      * <p>
  650.      * univariate derivatives are considered equal if they have the same derivatives.
  651.      * </p>
  652.      * @param other Object to test for equality to this
  653.      * @return true if two univariate derivatives are equal
  654.      */
  655.     @Override
  656.     public boolean equals(Object other) {

  657.         if (this == other) {
  658.             return true;
  659.         }

  660.         if (other instanceof Gradient) {
  661.             final Gradient rhs = (Gradient) other;
  662.             return value == rhs.value && MathArrays.equals(grad, rhs.grad);
  663.         }

  664.         return false;

  665.     }

  666.     /** Get a hashCode for the univariate derivative.
  667.      * @return a hash code value for this object
  668.      */
  669.     @Override
  670.     public int hashCode() {
  671.         return 129 + 7 * Double.hashCode(value) - 15 * Arrays.hashCode(grad);
  672.     }

  673. }