SparseGradient.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      https://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */

  17. /*
  18.  * This is not the original file distributed by the Apache Software Foundation
  19.  * It has been modified by the Hipparchus project
  20.  */
  21. package org.hipparchus.analysis.differentiation;

  22. import java.io.Serializable;
  23. import java.util.Collections;
  24. import java.util.HashMap;
  25. import java.util.Map;

  26. import org.hipparchus.Field;
  27. import org.hipparchus.exception.MathIllegalArgumentException;
  28. import org.hipparchus.util.FastMath;
  29. import org.hipparchus.util.MathArrays;
  30. import org.hipparchus.util.MathUtils;
  31. import org.hipparchus.util.Precision;

  32. /**
  33.  * First derivative computation with large number of variables.
  34.  * <p>
  35.  * This class plays a similar role to {@link DerivativeStructure}, with
  36.  * a focus on efficiency when dealing with large number of independent variables
  37.  * and most computation depend only on a few of them, and when only first derivative
  38.  * is desired. When these conditions are met, this class should be much faster than
  39.  * {@link DerivativeStructure} and use less memory.
  40.  * </p>
  41.  *
  42.  */
  43. public class SparseGradient implements Derivative1<SparseGradient>, Serializable {

  44.     /** Serializable UID. */
  45.     private static final long serialVersionUID = 20131025L;

  46.     /** Value of the calculation. */
  47.     private double value;

  48.     /** Stored derivative, each key representing a different independent variable. */
  49.     private final Map<Integer, Double> derivatives;

  50.     /** Internal constructor.
  51.      * @param value value of the function
  52.      * @param derivatives derivatives map, a deep copy will be performed,
  53.      * so the map given here will remain safe from changes in the new instance,
  54.      * may be null to create an empty derivatives map, i.e. a constant value
  55.      */
  56.     private SparseGradient(final double value, final Map<Integer, Double> derivatives) {
  57.         this.value = value;
  58.         this.derivatives = new HashMap<>();
  59.         if (derivatives != null) {
  60.             this.derivatives.putAll(derivatives);
  61.         }
  62.     }

  63.     /** Internal constructor.
  64.      * @param value value of the function
  65.      * @param scale scaling factor to apply to all derivatives
  66.      * @param derivatives derivatives map, a deep copy will be performed,
  67.      * so the map given here will remain safe from changes in the new instance,
  68.      * may be null to create an empty derivatives map, i.e. a constant value
  69.      */
  70.     private SparseGradient(final double value, final double scale,
  71.                              final Map<Integer, Double> derivatives) {
  72.         this.value = value;
  73.         this.derivatives = new HashMap<>();
  74.         if (derivatives != null) {
  75.             for (final Map.Entry<Integer, Double> entry : derivatives.entrySet()) {
  76.                 this.derivatives.put(entry.getKey(), scale * entry.getValue());
  77.             }
  78.         }
  79.     }

  80.     /** {@inheritDoc} */
  81.     @Override
  82.     public int getFreeParameters() {
  83.         return derivatives.size();
  84.     }

  85.     /** {@inheritDoc} */
  86.     @Override
  87.     public double getPartialDerivative(int... orders) throws MathIllegalArgumentException {
  88.         return getDerivative(orders[0]);
  89.     }

  90.     /** {@inheritDoc} */
  91.     @Override
  92.     public SparseGradient newInstance(final double v) {
  93.         return createConstant(v);
  94.     }

  95.     /** {@inheritDoc} */
  96.     @Override
  97.     public SparseGradient withValue(final double v) {
  98.         return new SparseGradient(v, derivatives);
  99.     }

  100.     /** Factory method creating a constant.
  101.      * @param value value of the constant
  102.      * @return a new instance
  103.      */
  104.     public static SparseGradient createConstant(final double value) {
  105.         return new SparseGradient(value, Collections.emptyMap());
  106.     }

  107.     /** Factory method creating an independent variable.
  108.      * @param idx index of the variable
  109.      * @param value value of the variable
  110.      * @return a new instance
  111.      */
  112.     public static SparseGradient createVariable(final int idx, final double value) {
  113.         return new SparseGradient(value, Collections.singletonMap(idx, 1.0));
  114.     }

  115.     /**
  116.      * Get the derivative with respect to a particular index variable.
  117.      *
  118.      * @param index index to differentiate with.
  119.      * @return derivative with respect to a particular index variable
  120.      */
  121.     public double getDerivative(final int index) {
  122.         final Double out = derivatives.get(index);
  123.         return (out == null) ? 0.0 : out;
  124.     }

  125.     /** {@inheritDoc} */
  126.     @Override
  127.     public SparseGradient getAddendum() {
  128.         return new SparseGradient(0, derivatives);
  129.     }

  130.     /**
  131.      * Get the value of the function.
  132.      * @return value of the function.
  133.      */
  134.     @Override
  135.     public double getValue() {
  136.         return value;
  137.     }

  138.     /** {@inheritDoc} */
  139.     @Override
  140.     public SparseGradient add(final SparseGradient a) {
  141.         final SparseGradient out = new SparseGradient(value + a.value, derivatives);
  142.         for (Map.Entry<Integer, Double> entry : a.derivatives.entrySet()) {
  143.             final int id = entry.getKey();
  144.             out.derivatives.merge(id, entry.getValue(), Double::sum);
  145.         }

  146.         return out;
  147.     }

  148.     /**
  149.      * Add in place.
  150.      * <p>
  151.      * This method is designed to be faster when used multiple times in a loop.
  152.      * </p>
  153.      * <p>
  154.      * The instance is changed here, in order to not change the
  155.      * instance the {@link #add(SparseGradient)} method should
  156.      * be used.
  157.      * </p>
  158.      * @param a instance to add
  159.      */
  160.     public void addInPlace(final SparseGradient a) {
  161.         value += a.value;
  162.         for (final Map.Entry<Integer, Double> entry : a.derivatives.entrySet()) {
  163.             final int id = entry.getKey();
  164.             derivatives.merge(id, entry.getValue(), Double::sum);
  165.         }
  166.     }

  167.     /** {@inheritDoc} */
  168.     @Override
  169.     public SparseGradient subtract(final SparseGradient a) {
  170.         final SparseGradient out = new SparseGradient(value - a.value, derivatives);
  171.         for (Map.Entry<Integer, Double> entry : a.derivatives.entrySet()) {
  172.             final int id = entry.getKey();
  173.             final Double old = out.derivatives.get(id);
  174.             if (old == null) {
  175.                 out.derivatives.put(id, -entry.getValue());
  176.             } else {
  177.                 out.derivatives.put(id, old - entry.getValue());
  178.             }
  179.         }
  180.         return out;
  181.     }

  182.     /** {@inheritDoc} */
  183.     @Override
  184.     public SparseGradient multiply(final SparseGradient a) {
  185.         final SparseGradient out =
  186.             new SparseGradient(value * a.value, Collections.emptyMap());

  187.         // Derivatives.
  188.         for (Map.Entry<Integer, Double> entry : derivatives.entrySet()) {
  189.             out.derivatives.put(entry.getKey(), a.value * entry.getValue());
  190.         }
  191.         for (Map.Entry<Integer, Double> entry : a.derivatives.entrySet()) {
  192.             final int id = entry.getKey();
  193.             out.derivatives.merge(id, value * entry.getValue(), Double::sum);
  194.         }
  195.         return out;
  196.     }

  197.     /**
  198.      * Multiply in place.
  199.      * <p>
  200.      * This method is designed to be faster when used multiple times in a loop.
  201.      * </p>
  202.      * <p>
  203.      * The instance is changed here, in order to not change the
  204.      * instance the {@link #add(SparseGradient)} method should
  205.      * be used.
  206.      * </p>
  207.      * @param a instance to multiply
  208.      */
  209.     public void multiplyInPlace(final SparseGradient a) {
  210.         // Derivatives.
  211.         derivatives.replaceAll((k, v) -> a.value * v);
  212.         for (Map.Entry<Integer, Double> entry : a.derivatives.entrySet()) {
  213.             final int id = entry.getKey();
  214.             derivatives.merge(id, value * entry.getValue(), Double::sum);
  215.         }
  216.         value *= a.value;
  217.     }

  218.     /** {@inheritDoc} */
  219.     @Override
  220.     public SparseGradient multiply(final double c) {
  221.         return new SparseGradient(value * c, c, derivatives);
  222.     }

  223.     /** {@inheritDoc} */
  224.     @Override
  225.     public SparseGradient multiply(final int n) {
  226.         return new SparseGradient(value * n, n, derivatives);
  227.     }

  228.     /** {@inheritDoc} */
  229.     @Override
  230.     public SparseGradient divide(final SparseGradient a) {
  231.         final SparseGradient out = new SparseGradient(value / a.value, Collections.emptyMap());

  232.         // Derivatives.
  233.         for (Map.Entry<Integer, Double> entry : derivatives.entrySet()) {
  234.             out.derivatives.put(entry.getKey(), entry.getValue() / a.value);
  235.         }
  236.         for (Map.Entry<Integer, Double> entry : a.derivatives.entrySet()) {
  237.             final int id = entry.getKey();
  238.             final Double old = out.derivatives.get(id);
  239.             if (old == null) {
  240.                 out.derivatives.put(id, -out.value / a.value * entry.getValue());
  241.             } else {
  242.                 out.derivatives.put(id, old - out.value / a.value * entry.getValue());
  243.             }
  244.         }
  245.         return out;
  246.     }

  247.     /** {@inheritDoc} */
  248.     @Override
  249.     public SparseGradient divide(final double c) {
  250.         return new SparseGradient(value / c, 1.0 / c, derivatives);
  251.     }

  252.     /** {@inheritDoc} */
  253.     @Override
  254.     public SparseGradient negate() {
  255.         return new SparseGradient(-value, -1.0, derivatives);
  256.     }

  257.     /** {@inheritDoc} */
  258.     @Override
  259.     public Field<SparseGradient> getField() {
  260.         return SparseGradientField.getInstance();
  261.     }

  262.     /** Local field for sparse gradient. */
  263.     private static class SparseGradientField implements Field<SparseGradient> {

  264.         /** Zero constant. */
  265.         private final SparseGradient zero;

  266.         /** One constant. */
  267.         private final SparseGradient one;

  268.         /** Private constructor for the singleton.
  269.          */
  270.         private SparseGradientField() {
  271.             zero = createConstant(0);
  272.             one  = createConstant(1);
  273.         }

  274.         /** Get the unique instance.
  275.          * @return the unique instance
  276.          */
  277.         public static SparseGradientField getInstance() {
  278.             return LazyHolder.INSTANCE;
  279.         }

  280.         /** {@inheritDoc} */
  281.         @Override
  282.         public SparseGradient getZero() {
  283.             return zero;
  284.         }

  285.         /** {@inheritDoc} */
  286.         @Override
  287.         public SparseGradient getOne() {
  288.             return one;
  289.         }

  290.         /** {@inheritDoc} */
  291.         @Override
  292.         public Class<SparseGradient> getRuntimeClass() {
  293.             return SparseGradient.class;
  294.         }

  295.         /** {@inheritDoc} */
  296.         @Override
  297.         public boolean equals(final Object other) {
  298.             return this == other;
  299.         }

  300.         /** {@inheritDoc} */
  301.         @Override
  302.         public int hashCode() {
  303.             return 0x142aeff7;
  304.         }

  305.         // CHECKSTYLE: stop HideUtilityClassConstructor
  306.         /** Holder for the instance.
  307.          * <p>We use here the Initialization On Demand Holder Idiom.</p>
  308.          */
  309.         private static class LazyHolder {
  310.             /** Cached field instance. */
  311.             private static final SparseGradientField INSTANCE = new SparseGradientField();
  312.         }
  313.         // CHECKSTYLE: resume HideUtilityClassConstructor

  314.     }

  315.     /** {@inheritDoc} */
  316.     @Override
  317.     public SparseGradient remainder(final double a) {
  318.         return new SparseGradient(FastMath.IEEEremainder(value, a), derivatives);
  319.     }

  320.     /** {@inheritDoc} */
  321.     @Override
  322.     public SparseGradient remainder(final SparseGradient a) {

  323.         // compute k such that lhs % rhs = lhs - k rhs
  324.         final double rem = FastMath.IEEEremainder(value, a.value);
  325.         final double k   = FastMath.rint((value - rem) / a.value);

  326.         return subtract(a.multiply(k));

  327.     }

  328.     /** {@inheritDoc} */
  329.     @Override
  330.     public SparseGradient abs() {
  331.         if (Double.doubleToLongBits(value) < 0) {
  332.             // we use the bits representation to also handle -0.0
  333.             return negate();
  334.         } else {
  335.             return this;
  336.         }
  337.     }

  338.     /** {@inheritDoc} */
  339.     @Override
  340.     public SparseGradient copySign(final SparseGradient sign) {
  341.         final long m = Double.doubleToLongBits(value);
  342.         final long s = Double.doubleToLongBits(sign.value);
  343.         if ((m >= 0 && s >= 0) || (m < 0 && s < 0)) { // Sign is currently OK
  344.             return this;
  345.         }
  346.         return negate(); // flip sign
  347.     }

  348.     /** {@inheritDoc} */
  349.     @Override
  350.     public SparseGradient copySign(final double sign) {
  351.         final long m = Double.doubleToLongBits(value);
  352.         final long s = Double.doubleToLongBits(sign);
  353.         if ((m >= 0 && s >= 0) || (m < 0 && s < 0)) { // Sign is currently OK
  354.             return this;
  355.         }
  356.         return negate(); // flip sign
  357.     }

  358.     /** {@inheritDoc} */
  359.     @Override
  360.     public SparseGradient scalb(final int n) {
  361.         final SparseGradient out = new SparseGradient(FastMath.scalb(value, n), Collections.emptyMap());
  362.         for (Map.Entry<Integer, Double> entry : derivatives.entrySet()) {
  363.             out.derivatives.put(entry.getKey(), FastMath.scalb(entry.getValue(), n));
  364.         }
  365.         return out;
  366.     }

  367.     /** {@inheritDoc} */
  368.     @Override
  369.     public SparseGradient hypot(final SparseGradient y) {
  370.         if (Double.isInfinite(value) || Double.isInfinite(y.value)) {
  371.             return createConstant(Double.POSITIVE_INFINITY);
  372.         } else if (Double.isNaN(value) || Double.isNaN(y.value)) {
  373.             return createConstant(Double.NaN);
  374.         } else {

  375.             final int expX = FastMath.getExponent(value);
  376.             final int expY = FastMath.getExponent(y.value);
  377.             if (expX > expY + 27) {
  378.                 // y is negligible with respect to x
  379.                 return abs();
  380.             } else if (expY > expX + 27) {
  381.                 // x is negligible with respect to y
  382.                 return y.abs();
  383.             } else {

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

  386.                 // scale parameters without losing precision
  387.                 final SparseGradient scaledX = scalb(-middleExp);
  388.                 final SparseGradient scaledY = y.scalb(-middleExp);

  389.                 // compute scaled hypotenuse
  390.                 final SparseGradient scaledH =
  391.                         scaledX.multiply(scaledX).add(scaledY.multiply(scaledY)).sqrt();

  392.                 // remove scaling
  393.                 return scaledH.scalb(middleExp);

  394.             }

  395.         }
  396.     }

  397.     /**
  398.      * Returns the hypotenuse of a triangle with sides {@code x} and {@code y}
  399.      * - sqrt(<i>x</i><sup>2</sup>&nbsp;+<i>y</i><sup>2</sup>)
  400.      * avoiding intermediate overflow or underflow.
  401.      *
  402.      * <ul>
  403.      * <li> If either argument is infinite, then the result is positive infinity.</li>
  404.      * <li> else, if either argument is NaN then the result is NaN.</li>
  405.      * </ul>
  406.      *
  407.      * @param x a value
  408.      * @param y a value
  409.      * @return sqrt(<i>x</i><sup>2</sup>&nbsp;+<i>y</i><sup>2</sup>)
  410.      */
  411.     public static SparseGradient hypot(final SparseGradient x, final SparseGradient y) {
  412.         return x.hypot(y);
  413.     }

  414.     /** {@inheritDoc} */
  415.     @Override
  416.     public SparseGradient sqrt() {
  417.         final double sqrt = FastMath.sqrt(value);
  418.         return new SparseGradient(sqrt, 0.5 / sqrt, derivatives);
  419.     }

  420.     /** {@inheritDoc} */
  421.     @Override
  422.     public SparseGradient pow(final double p) {
  423.         return new SparseGradient(FastMath.pow(value,  p), p * FastMath.pow(value,  p - 1), derivatives);
  424.     }

  425.     /** {@inheritDoc} */
  426.     @Override
  427.     public SparseGradient pow(final int n) {
  428.         if (n == 0) {
  429.             return getField().getOne();
  430.         } else {
  431.             final double valueNm1 = FastMath.pow(value,  n - 1);
  432.             return new SparseGradient(value * valueNm1, n * valueNm1, derivatives);
  433.         }
  434.     }

  435.     /** Compute a<sup>x</sup> where a is a double and x a {@link SparseGradient}
  436.      * @param a number to exponentiate
  437.      * @param x power to apply
  438.      * @return a<sup>x</sup>
  439.      */
  440.     public static SparseGradient pow(final double a, final SparseGradient x) {
  441.         if (a == 0) {
  442.             if (x.value == 0) {
  443.                 return x.compose(1.0, Double.NEGATIVE_INFINITY);
  444.             } else if (x.value < 0) {
  445.                 return x.compose(Double.NaN, Double.NaN);
  446.             } else {
  447.                 return x.getField().getZero();
  448.             }
  449.         } else {
  450.             final double ax = FastMath.pow(a, x.value);
  451.             return new SparseGradient(ax, ax * FastMath.log(a), x.derivatives);
  452.         }
  453.     }

  454.     /** {@inheritDoc} */
  455.     @Override
  456.     public SparseGradient atan2(final SparseGradient x) {

  457.         // compute r = sqrt(x^2+y^2)
  458.         final SparseGradient r = square().add(x.square()).sqrt();

  459.         final SparseGradient a;
  460.         if (x.value >= 0) {

  461.             // compute atan2(y, x) = 2 atan(y / (r + x))
  462.             a = divide(r.add(x)).atan().multiply(2);

  463.         } else {

  464.             // compute atan2(y, x) = +/- pi - 2 atan(y / (r - x))
  465.             final SparseGradient tmp = divide(r.subtract(x)).atan().multiply(-2);
  466.             a = tmp.add(tmp.value <= 0 ? -FastMath.PI : FastMath.PI);

  467.         }

  468.         // fix value to take special cases (+0/+0, +0/-0, -0/+0, -0/-0, +/-infinity) correctly
  469.         a.value = FastMath.atan2(value, x.value);

  470.         return a;

  471.     }

  472.     /** Two arguments arc tangent operation.
  473.      * @param y first argument of the arc tangent
  474.      * @param x second argument of the arc tangent
  475.      * @return atan2(y, x)
  476.      */
  477.     public static SparseGradient atan2(final SparseGradient y, final SparseGradient x) {
  478.         return y.atan2(x);
  479.     }

  480.     /** {@inheritDoc} */
  481.     @Override
  482.     public SparseGradient toDegrees() {
  483.         return new SparseGradient(FastMath.toDegrees(value), FastMath.toDegrees(1.0), derivatives);
  484.     }

  485.     /** {@inheritDoc} */
  486.     @Override
  487.     public SparseGradient toRadians() {
  488.         return new SparseGradient(FastMath.toRadians(value), FastMath.toRadians(1.0), derivatives);
  489.     }

  490.     /** Evaluate Taylor expansion of a sparse gradient.
  491.      * @param delta parameters offsets (&Delta;x, &Delta;y, ...)
  492.      * @return value of the Taylor expansion at x + &Delta;x, y + &Delta;y, ...
  493.      */
  494.     public double taylor(final double ... delta) {
  495.         double y = value;
  496.         for (int i = 0; i < delta.length; ++i) {
  497.             y += delta[i] * getDerivative(i);
  498.         }
  499.         return y;
  500.     }

  501.     /** Compute composition of the instance by a univariate function.
  502.      * @param f array of value and derivatives of the function at
  503.      * the current point (i.e. [f({@link #getValue()}),
  504.      * f'({@link #getValue()}), f''({@link #getValue()})...]).
  505.      * @return f(this)
  506.      * @exception MathIllegalArgumentException if the number of elements
  507.      * in the array is not equal to 2 (i.e. value and first derivative)
  508.      */
  509.     @Override
  510.     public SparseGradient compose(final double... f) {
  511.         MathUtils.checkDimension(f.length, 2);
  512.         return compose(f[0], f[1]);
  513.     }

  514.     /** {@inheritDoc} */
  515.     @Override
  516.     public SparseGradient compose(final double f0, final double f1) {
  517.         return new SparseGradient(f0, f1, derivatives);
  518.     }

  519.     /** {@inheritDoc} */
  520.     @Override
  521.     public SparseGradient linearCombination(final SparseGradient[] a,
  522.                                             final SparseGradient[] b)
  523.         throws MathIllegalArgumentException {

  524.         // compute a simple value, with all l derivatives
  525.         SparseGradient out = a[0].getField().getZero();
  526.         for (int i = 0; i < a.length; ++i) {
  527.             out = out.add(a[i].multiply(b[i]));
  528.         }

  529.         // recompute an accurate value, taking care of cancellations
  530.         final double[] aDouble = new double[a.length];
  531.         for (int i = 0; i < a.length; ++i) {
  532.             aDouble[i] = a[i].getValue();
  533.         }
  534.         final double[] bDouble = new double[b.length];
  535.         for (int i = 0; i < b.length; ++i) {
  536.             bDouble[i] = b[i].getValue();
  537.         }
  538.         out.value = MathArrays.linearCombination(aDouble, bDouble);

  539.         return out;

  540.     }

  541.     /** {@inheritDoc} */
  542.     @Override
  543.     public SparseGradient linearCombination(final double[] a, final SparseGradient[] b) {

  544.         // compute a simple value, with all partial derivatives
  545.         SparseGradient out = b[0].getField().getZero();
  546.         for (int i = 0; i < a.length; ++i) {
  547.             out = out.add(b[i].multiply(a[i]));
  548.         }

  549.         // recompute an accurate value, taking care of cancellations
  550.         final double[] bDouble = new double[b.length];
  551.         for (int i = 0; i < b.length; ++i) {
  552.             bDouble[i] = b[i].getValue();
  553.         }
  554.         out.value = MathArrays.linearCombination(a, bDouble);

  555.         return out;

  556.     }

  557.     /** {@inheritDoc} */
  558.     @Override
  559.     public SparseGradient linearCombination(final SparseGradient a1, final SparseGradient b1,
  560.                                               final SparseGradient a2, final SparseGradient b2) {

  561.         // compute a simple value, with all partial derivatives
  562.         SparseGradient out = a1.multiply(b1).add(a2.multiply(b2));

  563.         // recompute an accurate value, taking care of cancellations
  564.         out.value = MathArrays.linearCombination(a1.value, b1.value, a2.value, b2.value);

  565.         return out;

  566.     }

  567.     /** {@inheritDoc} */
  568.     @Override
  569.     public SparseGradient linearCombination(final double a1, final SparseGradient b1,
  570.                                               final double a2, final SparseGradient b2) {

  571.         // compute a simple value, with all partial derivatives
  572.         SparseGradient out = b1.multiply(a1).add(b2.multiply(a2));

  573.         // recompute an accurate value, taking care of cancellations
  574.         out.value = MathArrays.linearCombination(a1, b1.value, a2, b2.value);

  575.         return out;

  576.     }

  577.     /** {@inheritDoc} */
  578.     @Override
  579.     public SparseGradient linearCombination(final SparseGradient a1, final SparseGradient b1,
  580.                                               final SparseGradient a2, final SparseGradient b2,
  581.                                               final SparseGradient a3, final SparseGradient b3) {

  582.         // compute a simple value, with all partial derivatives
  583.         SparseGradient out = a1.multiply(b1).add(a2.multiply(b2)).add(a3.multiply(b3));

  584.         // recompute an accurate value, taking care of cancellations
  585.         out.value = MathArrays.linearCombination(a1.value, b1.value,
  586.                                                  a2.value, b2.value,
  587.                                                  a3.value, b3.value);

  588.         return out;

  589.     }

  590.     /** {@inheritDoc} */
  591.     @Override
  592.     public SparseGradient linearCombination(final double a1, final SparseGradient b1,
  593.                                               final double a2, final SparseGradient b2,
  594.                                               final double a3, final SparseGradient b3) {

  595.         // compute a simple value, with all partial derivatives
  596.         SparseGradient out = b1.multiply(a1).add(b2.multiply(a2)).add(b3.multiply(a3));

  597.         // recompute an accurate value, taking care of cancellations
  598.         out.value = MathArrays.linearCombination(a1, b1.value,
  599.                                                  a2, b2.value,
  600.                                                  a3, b3.value);

  601.         return out;

  602.     }

  603.     /** {@inheritDoc} */
  604.     @Override
  605.     public SparseGradient linearCombination(final SparseGradient a1, final SparseGradient b1,
  606.                                               final SparseGradient a2, final SparseGradient b2,
  607.                                               final SparseGradient a3, final SparseGradient b3,
  608.                                               final SparseGradient a4, final SparseGradient b4) {

  609.         // compute a simple value, with all partial derivatives
  610.         SparseGradient out = a1.multiply(b1).add(a2.multiply(b2)).add(a3.multiply(b3)).add(a4.multiply(b4));

  611.         // recompute an accurate value, taking care of cancellations
  612.         out.value = MathArrays.linearCombination(a1.value, b1.value,
  613.                                                  a2.value, b2.value,
  614.                                                  a3.value, b3.value,
  615.                                                  a4.value, b4.value);

  616.         return out;

  617.     }

  618.     /** {@inheritDoc} */
  619.     @Override
  620.     public SparseGradient linearCombination(final double a1, final SparseGradient b1,
  621.                                               final double a2, final SparseGradient b2,
  622.                                               final double a3, final SparseGradient b3,
  623.                                               final double a4, final SparseGradient b4) {

  624.         // compute a simple value, with all partial derivatives
  625.         SparseGradient out = b1.multiply(a1).add(b2.multiply(a2)).add(b3.multiply(a3)).add(b4.multiply(a4));

  626.         // recompute an accurate value, taking care of cancellations
  627.         out.value = MathArrays.linearCombination(a1, b1.value,
  628.                                                  a2, b2.value,
  629.                                                  a3, b3.value,
  630.                                                  a4, b4.value);

  631.         return out;

  632.     }

  633.     /** {@inheritDoc} */
  634.     @Override
  635.     public SparseGradient getPi() {
  636.         return new SparseGradient(FastMath.PI, null);
  637.     }

  638.     /**
  639.      * Test for the equality of two sparse gradients.
  640.      * <p>
  641.      * Sparse gradients are considered equal if they have the same value
  642.      * and the same derivatives.
  643.      * </p>
  644.      * @param other Object to test for equality to this
  645.      * @return true if two sparse gradients are equal
  646.      */
  647.     @Override
  648.     public boolean equals(Object other) {

  649.         if (this == other) {
  650.             return true;
  651.         }

  652.         if (other instanceof SparseGradient) {
  653.             final SparseGradient rhs = (SparseGradient)other;
  654.             if (!Precision.equals(value, rhs.value, 1)) {
  655.                 return false;
  656.             }
  657.             if (derivatives.size() != rhs.derivatives.size()) {
  658.                 return false;
  659.             }
  660.             for (final Map.Entry<Integer, Double> entry : derivatives.entrySet()) {
  661.                 if (!rhs.derivatives.containsKey(entry.getKey())) {
  662.                     return false;
  663.                 }
  664.                 if (!Precision.equals(entry.getValue(), rhs.derivatives.get(entry.getKey()), 1)) {
  665.                     return false;
  666.                 }
  667.             }
  668.             return true;
  669.         }

  670.         return false;

  671.     }

  672.     /**
  673.      * Get a hashCode for the derivative structure.
  674.      * @return a hash code value for this object
  675.      */
  676.     @Override
  677.     public int hashCode() {
  678.         return 743 + 809 * MathUtils.hash(value) + 167 * derivatives.hashCode();
  679.     }

  680. }