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.  *      http://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.apache.commons.math4.legacy.analysis.differentiation;

  18. import java.util.Collections;
  19. import java.util.HashMap;
  20. import java.util.Map;

  21. import org.apache.commons.numbers.core.Sum;
  22. import org.apache.commons.numbers.core.Precision;
  23. import org.apache.commons.math4.legacy.core.Field;
  24. import org.apache.commons.math4.legacy.core.FieldElement;
  25. import org.apache.commons.math4.legacy.core.RealFieldElement;
  26. import org.apache.commons.math4.core.jdkmath.JdkMath;

  27. /**
  28.  * First derivative computation with large number of variables.
  29.  * <p>
  30.  * This class plays a similar role to {@link DerivativeStructure}, with
  31.  * a focus on efficiency when dealing with large number of independent variables
  32.  * and most computation depend only on a few of them, and when only first derivative
  33.  * is desired. When these conditions are met, this class should be much faster than
  34.  * {@link DerivativeStructure} and use less memory.
  35.  * </p>
  36.  *
  37.  * @since 3.3
  38.  */
  39. public final class SparseGradient implements RealFieldElement<SparseGradient> {
  40.     /** Value of the calculation. */
  41.     private double value;

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

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

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

  74.     /** Factory method creating a constant.
  75.      * @param value value of the constant
  76.      * @return a new instance
  77.      */
  78.     public static SparseGradient createConstant(final double value) {
  79.         return new SparseGradient(value, Collections.<Integer, Double>emptyMap());
  80.     }

  81.     /** Factory method creating an independent variable.
  82.      * @param idx index of the variable
  83.      * @param value value of the variable
  84.      * @return a new instance
  85.      */
  86.     public static SparseGradient createVariable(final int idx, final double value) {
  87.         return new SparseGradient(value, Collections.singletonMap(idx, 1.0));
  88.     }

  89.     /**
  90.      * Find the number of variables.
  91.      * @return number of variables
  92.      */
  93.     public int numVars() {
  94.         return derivatives.size();
  95.     }

  96.     /**
  97.      * Get the derivative with respect to a particular index variable.
  98.      *
  99.      * @param index index to differentiate with.
  100.      * @return derivative with respect to a particular index variable
  101.      */
  102.     public double getDerivative(final int index) {
  103.         final Double out = derivatives.get(index);
  104.         return (out == null) ? 0.0 : out;
  105.     }

  106.     /**
  107.      * Get the value of the function.
  108.      * @return value of the function.
  109.      */
  110.     public double getValue() {
  111.         return value;
  112.     }

  113.     /** {@inheritDoc} */
  114.     @Override
  115.     public double getReal() {
  116.         return value;
  117.     }

  118.     /** {@inheritDoc} */
  119.     @Override
  120.     public SparseGradient add(final SparseGradient a) {
  121.         final SparseGradient out = new SparseGradient(value + a.value, derivatives);
  122.         for (Map.Entry<Integer, Double> entry : a.derivatives.entrySet()) {
  123.             final int id = entry.getKey();
  124.             final Double old = out.derivatives.get(id);
  125.             if (old == null) {
  126.                 out.derivatives.put(id, entry.getValue());
  127.             } else {
  128.                 out.derivatives.put(id, old + entry.getValue());
  129.             }
  130.         }

  131.         return out;
  132.     }

  133.     /**
  134.      * Add in place.
  135.      * <p>
  136.      * This method is designed to be faster when used multiple times in a loop.
  137.      * </p>
  138.      * <p>
  139.      * The instance is changed here, in order to not change the
  140.      * instance the {@link #add(SparseGradient)} method should
  141.      * be used.
  142.      * </p>
  143.      * @param a instance to add
  144.      */
  145.     public void addInPlace(final SparseGradient a) {
  146.         value += a.value;
  147.         for (final Map.Entry<Integer, Double> entry : a.derivatives.entrySet()) {
  148.             final int id = entry.getKey();
  149.             final Double old = derivatives.get(id);
  150.             if (old == null) {
  151.                 derivatives.put(id, entry.getValue());
  152.             } else {
  153.                 derivatives.put(id, old + entry.getValue());
  154.             }
  155.         }
  156.     }

  157.     /** {@inheritDoc} */
  158.     @Override
  159.     public SparseGradient add(final double c) {
  160.         return new SparseGradient(value + c, derivatives);
  161.     }

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

  177.     /** {@inheritDoc} */
  178.     @Override
  179.     public SparseGradient subtract(double c) {
  180.         return new SparseGradient(value - c, derivatives);
  181.     }

  182.     /** {@inheritDoc} */
  183.     @Override
  184.     public SparseGradient multiply(final SparseGradient a) {
  185.         final SparseGradient out =
  186.             new SparseGradient(value * a.value, Collections.<Integer, Double>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.             final Double old = out.derivatives.get(id);
  194.             if (old == null) {
  195.                 out.derivatives.put(id, value * entry.getValue());
  196.             } else {
  197.                 out.derivatives.put(id, old + value * entry.getValue());
  198.             }
  199.         }
  200.         return out;
  201.     }

  202.     /**
  203.      * Multiply in place.
  204.      * <p>
  205.      * This method is designed to be faster when used multiple times in a loop.
  206.      * </p>
  207.      * <p>
  208.      * The instance is changed here, in order to not change the
  209.      * instance the {@link #add(SparseGradient)} method should
  210.      * be used.
  211.      * </p>
  212.      * @param a instance to multiply
  213.      */
  214.     public void multiplyInPlace(final SparseGradient a) {
  215.         // Derivatives.
  216.         for (Map.Entry<Integer, Double> entry : derivatives.entrySet()) {
  217.             derivatives.put(entry.getKey(), a.value * entry.getValue());
  218.         }
  219.         for (Map.Entry<Integer, Double> entry : a.derivatives.entrySet()) {
  220.             final int id = entry.getKey();
  221.             final Double old = derivatives.get(id);
  222.             if (old == null) {
  223.                 derivatives.put(id, value * entry.getValue());
  224.             } else {
  225.                 derivatives.put(id, old + value * entry.getValue());
  226.             }
  227.         }
  228.         value *= a.value;
  229.     }

  230.     /** {@inheritDoc} */
  231.     @Override
  232.     public SparseGradient multiply(final double c) {
  233.         return new SparseGradient(value * c, c, derivatives);
  234.     }

  235.     /** {@inheritDoc} */
  236.     @Override
  237.     public SparseGradient multiply(final int n) {
  238.         return new SparseGradient(value * n, n, derivatives);
  239.     }

  240.     /** {@inheritDoc} */
  241.     @Override
  242.     public SparseGradient divide(final SparseGradient a) {
  243.         final SparseGradient out = new SparseGradient(value / a.value, Collections.<Integer, Double>emptyMap());

  244.         // Derivatives.
  245.         for (Map.Entry<Integer, Double> entry : derivatives.entrySet()) {
  246.             out.derivatives.put(entry.getKey(), entry.getValue() / a.value);
  247.         }
  248.         for (Map.Entry<Integer, Double> entry : a.derivatives.entrySet()) {
  249.             final int id = entry.getKey();
  250.             final Double old = out.derivatives.get(id);
  251.             if (old == null) {
  252.                 out.derivatives.put(id, -out.value / a.value * entry.getValue());
  253.             } else {
  254.                 out.derivatives.put(id, old - out.value / a.value * entry.getValue());
  255.             }
  256.         }
  257.         return out;
  258.     }

  259.     /** {@inheritDoc} */
  260.     @Override
  261.     public SparseGradient divide(final double c) {
  262.         return new SparseGradient(value / c, 1.0 / c, derivatives);
  263.     }

  264.     /** {@inheritDoc} */
  265.     @Override
  266.     public SparseGradient negate() {
  267.         return new SparseGradient(-value, -1.0, derivatives);
  268.     }

  269.     /** {@inheritDoc} */
  270.     @Override
  271.     public Field<SparseGradient> getField() {
  272.         return new Field<SparseGradient>() {

  273.             /** {@inheritDoc} */
  274.             @Override
  275.             public SparseGradient getZero() {
  276.                 return createConstant(0);
  277.             }

  278.             /** {@inheritDoc} */
  279.             @Override
  280.             public SparseGradient getOne() {
  281.                 return createConstant(1);
  282.             }

  283.             /** {@inheritDoc} */
  284.             @Override
  285.             public Class<? extends FieldElement<SparseGradient>> getRuntimeClass() {
  286.                 return SparseGradient.class;
  287.             }
  288.         };
  289.     }

  290.     /** {@inheritDoc} */
  291.     @Override
  292.     public SparseGradient remainder(final double a) {
  293.         return new SparseGradient(JdkMath.IEEEremainder(value, a), derivatives);
  294.     }

  295.     /** {@inheritDoc} */
  296.     @Override
  297.     public SparseGradient remainder(final SparseGradient a) {

  298.         // compute k such that lhs % rhs = lhs - k rhs
  299.         final double rem = JdkMath.IEEEremainder(value, a.value);
  300.         final double k   = JdkMath.rint((value - rem) / a.value);

  301.         return subtract(a.multiply(k));
  302.     }

  303.     /** {@inheritDoc} */
  304.     @Override
  305.     public SparseGradient abs() {
  306.         if (Double.doubleToLongBits(value) < 0) {
  307.             // we use the bits representation to also handle -0.0
  308.             return negate();
  309.         } else {
  310.             return this;
  311.         }
  312.     }

  313.     /** {@inheritDoc} */
  314.     @Override
  315.     public SparseGradient ceil() {
  316.         return createConstant(JdkMath.ceil(value));
  317.     }

  318.     /** {@inheritDoc} */
  319.     @Override
  320.     public SparseGradient floor() {
  321.         return createConstant(JdkMath.floor(value));
  322.     }

  323.     /** {@inheritDoc} */
  324.     @Override
  325.     public SparseGradient rint() {
  326.         return createConstant(JdkMath.rint(value));
  327.     }

  328.     /** {@inheritDoc} */
  329.     @Override
  330.     public long round() {
  331.         return JdkMath.round(value);
  332.     }

  333.     /** {@inheritDoc} */
  334.     @Override
  335.     public SparseGradient signum() {
  336.         return createConstant(JdkMath.signum(value));
  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(JdkMath.scalb(value, n), Collections.<Integer, Double>emptyMap());
  362.         for (Map.Entry<Integer, Double> entry : derivatives.entrySet()) {
  363.             out.derivatives.put(entry.getKey(), JdkMath.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 = JdkMath.getExponent(value);
  376.             final int expY = JdkMath.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 reciprocal() {
  417.         return new SparseGradient(1.0 / value, -1.0 / (value * value), derivatives);
  418.     }

  419.     /** {@inheritDoc} */
  420.     @Override
  421.     public SparseGradient sqrt() {
  422.         final double sqrt = JdkMath.sqrt(value);
  423.         return new SparseGradient(sqrt, 0.5 / sqrt, derivatives);
  424.     }

  425.     /** {@inheritDoc} */
  426.     @Override
  427.     public SparseGradient cbrt() {
  428.         final double cbrt = JdkMath.cbrt(value);
  429.         return new SparseGradient(cbrt, 1.0 / (3 * cbrt * cbrt), derivatives);
  430.     }

  431.     /** {@inheritDoc} */
  432.     @Override
  433.     public SparseGradient rootN(final int n) {
  434.         if (n == 2) {
  435.             return sqrt();
  436.         } else if (n == 3) {
  437.             return cbrt();
  438.         } else {
  439.             final double root = JdkMath.pow(value, 1.0 / n);
  440.             return new SparseGradient(root, 1.0 / (n * JdkMath.pow(root, n - 1)), derivatives);
  441.         }
  442.     }

  443.     /** {@inheritDoc} */
  444.     @Override
  445.     public SparseGradient pow(final double p) {
  446.         return new SparseGradient(JdkMath.pow(value,  p), p * JdkMath.pow(value,  p - 1), derivatives);
  447.     }

  448.     /** {@inheritDoc} */
  449.     @Override
  450.     public SparseGradient pow(final int n) {
  451.         if (n == 0) {
  452.             return getField().getOne();
  453.         } else {
  454.             final double valueNm1 = JdkMath.pow(value,  n - 1);
  455.             return new SparseGradient(value * valueNm1, n * valueNm1, derivatives);
  456.         }
  457.     }

  458.     /** {@inheritDoc} */
  459.     @Override
  460.     public SparseGradient pow(final SparseGradient e) {
  461.         return log().multiply(e).exp();
  462.     }

  463.     /** Compute a<sup>x</sup> where a is a double and x a {@link SparseGradient}.
  464.      * @param a number to exponentiate
  465.      * @param x power to apply
  466.      * @return a<sup>x</sup>
  467.      */
  468.     public static SparseGradient pow(final double a, final SparseGradient x) {
  469.         if (a == 0) {
  470.             if (x.value == 0) {
  471.                 return x.compose(1.0, Double.NEGATIVE_INFINITY);
  472.             } else if (x.value < 0) {
  473.                 return x.compose(Double.NaN, Double.NaN);
  474.             } else {
  475.                 return x.getField().getZero();
  476.             }
  477.         } else {
  478.             final double ax = JdkMath.pow(a, x.value);
  479.             return new SparseGradient(ax, ax * JdkMath.log(a), x.derivatives);
  480.         }
  481.     }

  482.     /** {@inheritDoc} */
  483.     @Override
  484.     public SparseGradient exp() {
  485.         final double e = JdkMath.exp(value);
  486.         return new SparseGradient(e, e, derivatives);
  487.     }

  488.     /** {@inheritDoc} */
  489.     @Override
  490.     public SparseGradient expm1() {
  491.         return new SparseGradient(JdkMath.expm1(value), JdkMath.exp(value), derivatives);
  492.     }

  493.     /** {@inheritDoc} */
  494.     @Override
  495.     public SparseGradient log() {
  496.         return new SparseGradient(JdkMath.log(value), 1.0 / value, derivatives);
  497.     }

  498.     /** Base 10 logarithm.
  499.      * @return base 10 logarithm of the instance
  500.      */
  501.     @Override
  502.     public SparseGradient log10() {
  503.         return new SparseGradient(JdkMath.log10(value), 1.0 / (JdkMath.log(10.0) * value), derivatives);
  504.     }

  505.     /** {@inheritDoc} */
  506.     @Override
  507.     public SparseGradient log1p() {
  508.         return new SparseGradient(JdkMath.log1p(value), 1.0 / (1.0 + value), derivatives);
  509.     }

  510.     /** {@inheritDoc} */
  511.     @Override
  512.     public SparseGradient cos() {
  513.         return new SparseGradient(JdkMath.cos(value), -JdkMath.sin(value), derivatives);
  514.     }

  515.     /** {@inheritDoc} */
  516.     @Override
  517.     public SparseGradient sin() {
  518.         return new SparseGradient(JdkMath.sin(value), JdkMath.cos(value), derivatives);
  519.     }

  520.     /** {@inheritDoc} */
  521.     @Override
  522.     public SparseGradient tan() {
  523.         final double t = JdkMath.tan(value);
  524.         return new SparseGradient(t, 1 + t * t, derivatives);
  525.     }

  526.     /** {@inheritDoc} */
  527.     @Override
  528.     public SparseGradient acos() {
  529.         return new SparseGradient(JdkMath.acos(value), -1.0 / JdkMath.sqrt(1 - value * value), derivatives);
  530.     }

  531.     /** {@inheritDoc} */
  532.     @Override
  533.     public SparseGradient asin() {
  534.         return new SparseGradient(JdkMath.asin(value), 1.0 / JdkMath.sqrt(1 - value * value), derivatives);
  535.     }

  536.     /** {@inheritDoc} */
  537.     @Override
  538.     public SparseGradient atan() {
  539.         return new SparseGradient(JdkMath.atan(value), 1.0 / (1 + value * value), derivatives);
  540.     }

  541.     /** {@inheritDoc} */
  542.     @Override
  543.     public SparseGradient atan2(final SparseGradient x) {

  544.         // compute r = sqrt(x^2+y^2)
  545.         final SparseGradient r = multiply(this).add(x.multiply(x)).sqrt();

  546.         final SparseGradient a;
  547.         if (x.value >= 0) {

  548.             // compute atan2(y, x) = 2 atan(y / (r + x))
  549.             a = divide(r.add(x)).atan().multiply(2);
  550.         } else {

  551.             // compute atan2(y, x) = +/- pi - 2 atan(y / (r - x))
  552.             final SparseGradient tmp = divide(r.subtract(x)).atan().multiply(-2);
  553.             a = tmp.add(tmp.value <= 0 ? -JdkMath.PI : JdkMath.PI);
  554.         }

  555.         // fix value to take special cases (+0/+0, +0/-0, -0/+0, -0/-0, +/-infinity) correctly
  556.         a.value = JdkMath.atan2(value, x.value);

  557.         return a;
  558.     }

  559.     /** Two arguments arc tangent operation.
  560.      * @param y first argument of the arc tangent
  561.      * @param x second argument of the arc tangent
  562.      * @return atan2(y, x)
  563.      */
  564.     public static SparseGradient atan2(final SparseGradient y, final SparseGradient x) {
  565.         return y.atan2(x);
  566.     }

  567.     /** {@inheritDoc} */
  568.     @Override
  569.     public SparseGradient cosh() {
  570.         return new SparseGradient(JdkMath.cosh(value), JdkMath.sinh(value), derivatives);
  571.     }

  572.     /** {@inheritDoc} */
  573.     @Override
  574.     public SparseGradient sinh() {
  575.         return new SparseGradient(JdkMath.sinh(value), JdkMath.cosh(value), derivatives);
  576.     }

  577.     /** {@inheritDoc} */
  578.     @Override
  579.     public SparseGradient tanh() {
  580.         final double t = JdkMath.tanh(value);
  581.         return new SparseGradient(t, 1 - t * t, derivatives);
  582.     }

  583.     /** {@inheritDoc} */
  584.     @Override
  585.     public SparseGradient acosh() {
  586.         return new SparseGradient(JdkMath.acosh(value), 1.0 / JdkMath.sqrt(value * value - 1.0), derivatives);
  587.     }

  588.     /** {@inheritDoc} */
  589.     @Override
  590.     public SparseGradient asinh() {
  591.         return new SparseGradient(JdkMath.asinh(value), 1.0 / JdkMath.sqrt(value * value + 1.0), derivatives);
  592.     }

  593.     /** {@inheritDoc} */
  594.     @Override
  595.     public SparseGradient atanh() {
  596.         return new SparseGradient(JdkMath.atanh(value), 1.0 / (1.0 - value * value), derivatives);
  597.     }

  598.     /** Convert radians to degrees, with error of less than 0.5 ULP.
  599.      *  @return instance converted into degrees
  600.      */
  601.     public SparseGradient toDegrees() {
  602.         return new SparseGradient(JdkMath.toDegrees(value), JdkMath.toDegrees(1.0), derivatives);
  603.     }

  604.     /** Convert degrees to radians, with error of less than 0.5 ULP.
  605.      *  @return instance converted into radians
  606.      */
  607.     public SparseGradient toRadians() {
  608.         return new SparseGradient(JdkMath.toRadians(value), JdkMath.toRadians(1.0), derivatives);
  609.     }

  610.     /** Evaluate Taylor expansion of a sparse gradient.
  611.      * @param delta parameters offsets (&Delta;x, &Delta;y, ...)
  612.      * @return value of the Taylor expansion at x + &Delta;x, y + &Delta;y, ...
  613.      */
  614.     public double taylor(final double ... delta) {
  615.         double y = value;
  616.         for (int i = 0; i < delta.length; ++i) {
  617.             y += delta[i] * getDerivative(i);
  618.         }
  619.         return y;
  620.     }

  621.     /** Compute composition of the instance by a univariate function.
  622.      * @param f0 value of the function at (i.e. f({@link #getValue()}))
  623.      * @param f1 first derivative of the function at
  624.      * the current point (i.e. f'({@link #getValue()}))
  625.      * @return f(this)
  626.     */
  627.     public SparseGradient compose(final double f0, final double f1) {
  628.         return new SparseGradient(f0, f1, derivatives);
  629.     }

  630.     /** {@inheritDoc} */
  631.     @Override
  632.     public SparseGradient linearCombination(final SparseGradient[] a,
  633.                                             final SparseGradient[] b) {
  634.         // compute a simple value, with all partial derivatives
  635.         SparseGradient out = a[0].getField().getZero();
  636.         for (int i = 0; i < a.length; ++i) {
  637.             out = out.add(a[i].multiply(b[i]));
  638.         }

  639.         // recompute an accurate value, taking care of cancellations
  640.         final double[] aDouble = new double[a.length];
  641.         for (int i = 0; i < a.length; ++i) {
  642.             aDouble[i] = a[i].getValue();
  643.         }
  644.         final double[] bDouble = new double[b.length];
  645.         for (int i = 0; i < b.length; ++i) {
  646.             bDouble[i] = b[i].getValue();
  647.         }
  648.         out.value = Sum.ofProducts(aDouble, bDouble).getAsDouble();

  649.         return out;
  650.     }

  651.     /** {@inheritDoc} */
  652.     @Override
  653.     public SparseGradient linearCombination(final double[] a, final SparseGradient[] b) {

  654.         // compute a simple value, with all partial derivatives
  655.         SparseGradient out = b[0].getField().getZero();
  656.         for (int i = 0; i < a.length; ++i) {
  657.             out = out.add(b[i].multiply(a[i]));
  658.         }

  659.         // recompute an accurate value, taking care of cancellations
  660.         final double[] bDouble = new double[b.length];
  661.         for (int i = 0; i < b.length; ++i) {
  662.             bDouble[i] = b[i].getValue();
  663.         }
  664.         out.value = Sum.ofProducts(a, bDouble).getAsDouble();

  665.         return out;
  666.     }

  667.     /** {@inheritDoc} */
  668.     @Override
  669.     public SparseGradient linearCombination(final SparseGradient a1, final SparseGradient b1,
  670.                                             final SparseGradient a2, final SparseGradient b2) {

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

  673.         // recompute an accurate value, taking care of cancellations
  674.         out.value = Sum.create()
  675.             .addProduct(a1.value, b1.value)
  676.             .addProduct(a2.value, b2.value).getAsDouble();

  677.         return out;
  678.     }

  679.     /** {@inheritDoc} */
  680.     @Override
  681.     public SparseGradient linearCombination(final double a1, final SparseGradient b1,
  682.                                             final double a2, final SparseGradient b2) {

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

  685.         // recompute an accurate value, taking care of cancellations
  686.         out.value = Sum.create()
  687.             .addProduct(a1, b1.value)
  688.             .addProduct(a2, b2.value).getAsDouble();

  689.         return out;
  690.     }

  691.     /** {@inheritDoc} */
  692.     @Override
  693.     public SparseGradient linearCombination(final SparseGradient a1, final SparseGradient b1,
  694.                                             final SparseGradient a2, final SparseGradient b2,
  695.                                             final SparseGradient a3, final SparseGradient b3) {

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

  698.         // recompute an accurate value, taking care of cancellations
  699.         out.value = Sum.create()
  700.             .addProduct(a1.value, b1.value)
  701.             .addProduct(a2.value, b2.value)
  702.             .addProduct(a3.value, b3.value).getAsDouble();

  703.         return out;
  704.     }

  705.     /** {@inheritDoc} */
  706.     @Override
  707.     public SparseGradient linearCombination(final double a1, final SparseGradient b1,
  708.                                             final double a2, final SparseGradient b2,
  709.                                             final double a3, final SparseGradient b3) {

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

  712.         // recompute an accurate value, taking care of cancellations
  713.         out.value = Sum.create()
  714.             .addProduct(a1, b1.value)
  715.             .addProduct(a2, b2.value)
  716.             .addProduct(a3, b3.value).getAsDouble();

  717.         return out;
  718.     }

  719.     /** {@inheritDoc} */
  720.     @Override
  721.     public SparseGradient linearCombination(final SparseGradient a1, final SparseGradient b1,
  722.                                             final SparseGradient a2, final SparseGradient b2,
  723.                                             final SparseGradient a3, final SparseGradient b3,
  724.                                             final SparseGradient a4, final SparseGradient b4) {

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

  727.         // recompute an accurate value, taking care of cancellations
  728.         out.value = Sum.create()
  729.             .addProduct(a1.value, b1.value)
  730.             .addProduct(a2.value, b2.value)
  731.             .addProduct(a3.value, b3.value)
  732.             .addProduct(a4.value, b4.value).getAsDouble();

  733.         return out;
  734.     }

  735.     /** {@inheritDoc} */
  736.     @Override
  737.     public SparseGradient linearCombination(final double a1, final SparseGradient b1,
  738.                                               final double a2, final SparseGradient b2,
  739.                                               final double a3, final SparseGradient b3,
  740.                                               final double a4, final SparseGradient b4) {

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

  743.         // recompute an accurate value, taking care of cancellations
  744.         out.value = Sum.create()
  745.             .addProduct(a1, b1.value)
  746.             .addProduct(a2, b2.value)
  747.             .addProduct(a3, b3.value)
  748.             .addProduct(a4, b4.value).getAsDouble();

  749.         return out;
  750.     }

  751.     /**
  752.      * Test for the equality of two sparse gradients.
  753.      * <p>
  754.      * Sparse gradients are considered equal if they have the same value
  755.      * and the same derivatives.
  756.      * </p>
  757.      * @param other Object to test for equality to this
  758.      * @return true if two sparse gradients are equal
  759.      */
  760.     @Override
  761.     public boolean equals(Object other) {

  762.         if (this == other) {
  763.             return true;
  764.         }

  765.         if (other instanceof SparseGradient) {
  766.             final SparseGradient rhs = (SparseGradient)other;
  767.             if (!Precision.equals(value, rhs.value, 1)) {
  768.                 return false;
  769.             }
  770.             if (derivatives.size() != rhs.derivatives.size()) {
  771.                 return false;
  772.             }
  773.             for (final Map.Entry<Integer, Double> entry : derivatives.entrySet()) {
  774.                 if (!rhs.derivatives.containsKey(entry.getKey())) {
  775.                     return false;
  776.                 }
  777.                 if (!Precision.equals(entry.getValue(), rhs.derivatives.get(entry.getKey()), 1)) {
  778.                     return false;
  779.                 }
  780.             }
  781.             return true;
  782.         }

  783.         return false;
  784.     }

  785.     /**
  786.      * Get a hashCode for the derivative structure.
  787.      * @return a hash code value for this object
  788.      * @since 3.2
  789.      */
  790.     @Override
  791.     public int hashCode() {
  792.         return 743 + 809 * Double.hashCode(value) + 167 * derivatives.hashCode();
  793.     }
  794. }