001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.math3.analysis.differentiation; 018 019import java.io.Serializable; 020import java.util.Collections; 021import java.util.HashMap; 022import java.util.Map; 023 024import org.apache.commons.math3.Field; 025import org.apache.commons.math3.FieldElement; 026import org.apache.commons.math3.RealFieldElement; 027import org.apache.commons.math3.exception.DimensionMismatchException; 028import org.apache.commons.math3.util.FastMath; 029import org.apache.commons.math3.util.MathArrays; 030import org.apache.commons.math3.util.MathUtils; 031import org.apache.commons.math3.util.Precision; 032 033/** 034 * First derivative computation with large number of variables. 035 * <p> 036 * This class plays a similar role to {@link DerivativeStructure}, with 037 * a focus on efficiency when dealing with large number of independent variables 038 * and most computation depend only on a few of them, and when only first derivative 039 * is desired. When these conditions are met, this class should be much faster than 040 * {@link DerivativeStructure} and use less memory. 041 * </p> 042 * 043 * @since 3.3 044 */ 045public class SparseGradient implements RealFieldElement<SparseGradient>, Serializable { 046 047 /** Serializable UID. */ 048 private static final long serialVersionUID = 20131025L; 049 050 /** Value of the calculation. */ 051 private double value; 052 053 /** Stored derivative, each key representing a different independent variable. */ 054 private final Map<Integer, Double> derivatives; 055 056 /** Internal constructor. 057 * @param value value of the function 058 * @param derivatives derivatives map, a deep copy will be performed, 059 * so the map given here will remain safe from changes in the new instance, 060 * may be null to create an empty derivatives map, i.e. a constant value 061 */ 062 private SparseGradient(final double value, final Map<Integer, Double> derivatives) { 063 this.value = value; 064 this.derivatives = new HashMap<Integer, Double>(); 065 if (derivatives != null) { 066 this.derivatives.putAll(derivatives); 067 } 068 } 069 070 /** Internal constructor. 071 * @param value value of the function 072 * @param scale scaling factor to apply to all derivatives 073 * @param derivatives derivatives map, a deep copy will be performed, 074 * so the map given here will remain safe from changes in the new instance, 075 * may be null to create an empty derivatives map, i.e. a constant value 076 */ 077 private SparseGradient(final double value, final double scale, 078 final Map<Integer, Double> derivatives) { 079 this.value = value; 080 this.derivatives = new HashMap<Integer, Double>(); 081 if (derivatives != null) { 082 for (final Map.Entry<Integer, Double> entry : derivatives.entrySet()) { 083 this.derivatives.put(entry.getKey(), scale * entry.getValue()); 084 } 085 } 086 } 087 088 /** Factory method creating a constant. 089 * @param value value of the constant 090 * @return a new instance 091 */ 092 public static SparseGradient createConstant(final double value) { 093 return new SparseGradient(value, Collections.<Integer, Double> emptyMap()); 094 } 095 096 /** Factory method creating an independent variable. 097 * @param idx index of the variable 098 * @param value value of the variable 099 * @return a new instance 100 */ 101 public static SparseGradient createVariable(final int idx, final double value) { 102 return new SparseGradient(value, Collections.singletonMap(idx, 1.0)); 103 } 104 105 /** 106 * Find the number of variables. 107 * @return number of variables 108 */ 109 public int numVars() { 110 return derivatives.size(); 111 } 112 113 /** 114 * Get the derivative with respect to a particular index variable. 115 * 116 * @param index index to differentiate with. 117 * @return derivative with respect to a particular index variable 118 */ 119 public double getDerivative(final int index) { 120 final Double out = derivatives.get(index); 121 return (out == null) ? 0.0 : out; 122 } 123 124 /** 125 * Get the value of the function. 126 * @return value of the function. 127 */ 128 public double getValue() { 129 return value; 130 } 131 132 /** {@inheritDoc} */ 133 public double getReal() { 134 return value; 135 } 136 137 /** {@inheritDoc} */ 138 public SparseGradient add(final SparseGradient a) { 139 final SparseGradient out = new SparseGradient(value + a.value, derivatives); 140 for (Map.Entry<Integer, Double> entry : a.derivatives.entrySet()) { 141 final int id = entry.getKey(); 142 final Double old = out.derivatives.get(id); 143 if (old == null) { 144 out.derivatives.put(id, entry.getValue()); 145 } else { 146 out.derivatives.put(id, old + entry.getValue()); 147 } 148 } 149 150 return out; 151 } 152 153 /** 154 * Add in place. 155 * <p> 156 * This method is designed to be faster when used multiple times in a loop. 157 * </p> 158 * <p> 159 * The instance is changed here, in order to not change the 160 * instance the {@link #add(SparseGradient)} method should 161 * be used. 162 * </p> 163 * @param a instance to add 164 */ 165 public void addInPlace(final SparseGradient a) { 166 value += a.value; 167 for (final Map.Entry<Integer, Double> entry : a.derivatives.entrySet()) { 168 final int id = entry.getKey(); 169 final Double old = derivatives.get(id); 170 if (old == null) { 171 derivatives.put(id, entry.getValue()); 172 } else { 173 derivatives.put(id, old + entry.getValue()); 174 } 175 } 176 } 177 178 /** {@inheritDoc} */ 179 public SparseGradient add(final double c) { 180 final SparseGradient out = new SparseGradient(value + c, derivatives); 181 return out; 182 } 183 184 /** {@inheritDoc} */ 185 public SparseGradient subtract(final SparseGradient a) { 186 final SparseGradient out = new SparseGradient(value - a.value, derivatives); 187 for (Map.Entry<Integer, Double> entry : a.derivatives.entrySet()) { 188 final int id = entry.getKey(); 189 final Double old = out.derivatives.get(id); 190 if (old == null) { 191 out.derivatives.put(id, -entry.getValue()); 192 } else { 193 out.derivatives.put(id, old - entry.getValue()); 194 } 195 } 196 return out; 197 } 198 199 /** {@inheritDoc} */ 200 public SparseGradient subtract(double c) { 201 return new SparseGradient(value - c, derivatives); 202 } 203 204 /** {@inheritDoc} */ 205 public SparseGradient multiply(final SparseGradient a) { 206 final SparseGradient out = 207 new SparseGradient(value * a.value, Collections.<Integer, Double> emptyMap()); 208 209 // Derivatives. 210 for (Map.Entry<Integer, Double> entry : derivatives.entrySet()) { 211 out.derivatives.put(entry.getKey(), a.value * entry.getValue()); 212 } 213 for (Map.Entry<Integer, Double> entry : a.derivatives.entrySet()) { 214 final int id = entry.getKey(); 215 final Double old = out.derivatives.get(id); 216 if (old == null) { 217 out.derivatives.put(id, value * entry.getValue()); 218 } else { 219 out.derivatives.put(id, old + value * entry.getValue()); 220 } 221 } 222 return out; 223 } 224 225 /** 226 * Multiply in place. 227 * <p> 228 * This method is designed to be faster when used multiple times in a loop. 229 * </p> 230 * <p> 231 * The instance is changed here, in order to not change the 232 * instance the {@link #add(SparseGradient)} method should 233 * be used. 234 * </p> 235 * @param a instance to multiply 236 */ 237 public void multiplyInPlace(final SparseGradient a) { 238 // Derivatives. 239 for (Map.Entry<Integer, Double> entry : derivatives.entrySet()) { 240 derivatives.put(entry.getKey(), a.value * entry.getValue()); 241 } 242 for (Map.Entry<Integer, Double> entry : a.derivatives.entrySet()) { 243 final int id = entry.getKey(); 244 final Double old = derivatives.get(id); 245 if (old == null) { 246 derivatives.put(id, value * entry.getValue()); 247 } else { 248 derivatives.put(id, old + value * entry.getValue()); 249 } 250 } 251 value *= a.value; 252 } 253 254 /** {@inheritDoc} */ 255 public SparseGradient multiply(final double c) { 256 return new SparseGradient(value * c, c, derivatives); 257 } 258 259 /** {@inheritDoc} */ 260 public SparseGradient multiply(final int n) { 261 return new SparseGradient(value * n, n, derivatives); 262 } 263 264 /** {@inheritDoc} */ 265 public SparseGradient divide(final SparseGradient a) { 266 final SparseGradient out = new SparseGradient(value / a.value, Collections.<Integer, Double> emptyMap()); 267 268 // Derivatives. 269 for (Map.Entry<Integer, Double> entry : derivatives.entrySet()) { 270 out.derivatives.put(entry.getKey(), entry.getValue() / a.value); 271 } 272 for (Map.Entry<Integer, Double> entry : a.derivatives.entrySet()) { 273 final int id = entry.getKey(); 274 final Double old = out.derivatives.get(id); 275 if (old == null) { 276 out.derivatives.put(id, -out.value / a.value * entry.getValue()); 277 } else { 278 out.derivatives.put(id, old - out.value / a.value * entry.getValue()); 279 } 280 } 281 return out; 282 } 283 284 /** {@inheritDoc} */ 285 public SparseGradient divide(final double c) { 286 return new SparseGradient(value / c, 1.0 / c, derivatives); 287 } 288 289 /** {@inheritDoc} */ 290 public SparseGradient negate() { 291 return new SparseGradient(-value, -1.0, derivatives); 292 } 293 294 /** {@inheritDoc} */ 295 public Field<SparseGradient> getField() { 296 return new Field<SparseGradient>() { 297 298 /** {@inheritDoc} */ 299 public SparseGradient getZero() { 300 return createConstant(0); 301 } 302 303 /** {@inheritDoc} */ 304 public SparseGradient getOne() { 305 return createConstant(1); 306 } 307 308 /** {@inheritDoc} */ 309 public Class<? extends FieldElement<SparseGradient>> getRuntimeClass() { 310 return SparseGradient.class; 311 } 312 313 }; 314 } 315 316 /** {@inheritDoc} */ 317 public SparseGradient remainder(final double a) { 318 return new SparseGradient(FastMath.IEEEremainder(value, a), derivatives); 319 } 320 321 /** {@inheritDoc} */ 322 public SparseGradient remainder(final SparseGradient a) { 323 324 // compute k such that lhs % rhs = lhs - k rhs 325 final double rem = FastMath.IEEEremainder(value, a.value); 326 final double k = FastMath.rint((value - rem) / a.value); 327 328 return subtract(a.multiply(k)); 329 330 } 331 332 /** {@inheritDoc} */ 333 public SparseGradient abs() { 334 if (Double.doubleToLongBits(value) < 0) { 335 // we use the bits representation to also handle -0.0 336 return negate(); 337 } else { 338 return this; 339 } 340 } 341 342 /** {@inheritDoc} */ 343 public SparseGradient ceil() { 344 return createConstant(FastMath.ceil(value)); 345 } 346 347 /** {@inheritDoc} */ 348 public SparseGradient floor() { 349 return createConstant(FastMath.floor(value)); 350 } 351 352 /** {@inheritDoc} */ 353 public SparseGradient rint() { 354 return createConstant(FastMath.rint(value)); 355 } 356 357 /** {@inheritDoc} */ 358 public long round() { 359 return FastMath.round(value); 360 } 361 362 /** {@inheritDoc} */ 363 public SparseGradient signum() { 364 return createConstant(FastMath.signum(value)); 365 } 366 367 /** {@inheritDoc} */ 368 public SparseGradient copySign(final SparseGradient sign) { 369 final long m = Double.doubleToLongBits(value); 370 final long s = Double.doubleToLongBits(sign.value); 371 if ((m >= 0 && s >= 0) || (m < 0 && s < 0)) { // Sign is currently OK 372 return this; 373 } 374 return negate(); // flip sign 375 } 376 377 /** {@inheritDoc} */ 378 public SparseGradient copySign(final double sign) { 379 final long m = Double.doubleToLongBits(value); 380 final long s = Double.doubleToLongBits(sign); 381 if ((m >= 0 && s >= 0) || (m < 0 && s < 0)) { // Sign is currently OK 382 return this; 383 } 384 return negate(); // flip sign 385 } 386 387 /** {@inheritDoc} */ 388 public SparseGradient scalb(final int n) { 389 final SparseGradient out = new SparseGradient(FastMath.scalb(value, n), Collections.<Integer, Double> emptyMap()); 390 for (Map.Entry<Integer, Double> entry : derivatives.entrySet()) { 391 out.derivatives.put(entry.getKey(), FastMath.scalb(entry.getValue(), n)); 392 } 393 return out; 394 } 395 396 /** {@inheritDoc} */ 397 public SparseGradient hypot(final SparseGradient y) { 398 if (Double.isInfinite(value) || Double.isInfinite(y.value)) { 399 return createConstant(Double.POSITIVE_INFINITY); 400 } else if (Double.isNaN(value) || Double.isNaN(y.value)) { 401 return createConstant(Double.NaN); 402 } else { 403 404 final int expX = FastMath.getExponent(value); 405 final int expY = FastMath.getExponent(y.value); 406 if (expX > expY + 27) { 407 // y is negligible with respect to x 408 return abs(); 409 } else if (expY > expX + 27) { 410 // x is negligible with respect to y 411 return y.abs(); 412 } else { 413 414 // find an intermediate scale to avoid both overflow and underflow 415 final int middleExp = (expX + expY) / 2; 416 417 // scale parameters without losing precision 418 final SparseGradient scaledX = scalb(-middleExp); 419 final SparseGradient scaledY = y.scalb(-middleExp); 420 421 // compute scaled hypotenuse 422 final SparseGradient scaledH = 423 scaledX.multiply(scaledX).add(scaledY.multiply(scaledY)).sqrt(); 424 425 // remove scaling 426 return scaledH.scalb(middleExp); 427 428 } 429 430 } 431 } 432 433 /** 434 * Returns the hypotenuse of a triangle with sides {@code x} and {@code y} 435 * - sqrt(<i>x</i><sup>2</sup> +<i>y</i><sup>2</sup>) 436 * avoiding intermediate overflow or underflow. 437 * 438 * <ul> 439 * <li> If either argument is infinite, then the result is positive infinity.</li> 440 * <li> else, if either argument is NaN then the result is NaN.</li> 441 * </ul> 442 * 443 * @param x a value 444 * @param y a value 445 * @return sqrt(<i>x</i><sup>2</sup> +<i>y</i><sup>2</sup>) 446 */ 447 public static SparseGradient hypot(final SparseGradient x, final SparseGradient y) { 448 return x.hypot(y); 449 } 450 451 /** {@inheritDoc} */ 452 public SparseGradient reciprocal() { 453 return new SparseGradient(1.0 / value, -1.0 / (value * value), derivatives); 454 } 455 456 /** {@inheritDoc} */ 457 public SparseGradient sqrt() { 458 final double sqrt = FastMath.sqrt(value); 459 return new SparseGradient(sqrt, 0.5 / sqrt, derivatives); 460 } 461 462 /** {@inheritDoc} */ 463 public SparseGradient cbrt() { 464 final double cbrt = FastMath.cbrt(value); 465 return new SparseGradient(cbrt, 1.0 / (3 * cbrt * cbrt), derivatives); 466 } 467 468 /** {@inheritDoc} */ 469 public SparseGradient rootN(final int n) { 470 if (n == 2) { 471 return sqrt(); 472 } else if (n == 3) { 473 return cbrt(); 474 } else { 475 final double root = FastMath.pow(value, 1.0 / n); 476 return new SparseGradient(root, 1.0 / (n * FastMath.pow(root, n - 1)), derivatives); 477 } 478 } 479 480 /** {@inheritDoc} */ 481 public SparseGradient pow(final double p) { 482 return new SparseGradient(FastMath.pow(value, p), p * FastMath.pow(value, p - 1), derivatives); 483 } 484 485 /** {@inheritDoc} */ 486 public SparseGradient pow(final int n) { 487 if (n == 0) { 488 return getField().getOne(); 489 } else { 490 final double valueNm1 = FastMath.pow(value, n - 1); 491 return new SparseGradient(value * valueNm1, n * valueNm1, derivatives); 492 } 493 } 494 495 /** {@inheritDoc} */ 496 public SparseGradient pow(final SparseGradient e) { 497 return log().multiply(e).exp(); 498 } 499 500 /** Compute a<sup>x</sup> where a is a double and x a {@link SparseGradient} 501 * @param a number to exponentiate 502 * @param x power to apply 503 * @return a<sup>x</sup> 504 */ 505 public static SparseGradient pow(final double a, final SparseGradient x) { 506 if (a == 0) { 507 if (x.value == 0) { 508 return x.compose(1.0, Double.NEGATIVE_INFINITY); 509 } else if (x.value < 0) { 510 return x.compose(Double.NaN, Double.NaN); 511 } else { 512 return x.getField().getZero(); 513 } 514 } else { 515 final double ax = FastMath.pow(a, x.value); 516 return new SparseGradient(ax, ax * FastMath.log(a), x.derivatives); 517 } 518 } 519 520 /** {@inheritDoc} */ 521 public SparseGradient exp() { 522 final double e = FastMath.exp(value); 523 return new SparseGradient(e, e, derivatives); 524 } 525 526 /** {@inheritDoc} */ 527 public SparseGradient expm1() { 528 return new SparseGradient(FastMath.expm1(value), FastMath.exp(value), derivatives); 529 } 530 531 /** {@inheritDoc} */ 532 public SparseGradient log() { 533 return new SparseGradient(FastMath.log(value), 1.0 / value, derivatives); 534 } 535 536 /** Base 10 logarithm. 537 * @return base 10 logarithm of the instance 538 */ 539 public SparseGradient log10() { 540 return new SparseGradient(FastMath.log10(value), 1.0 / (FastMath.log(10.0) * value), derivatives); 541 } 542 543 /** {@inheritDoc} */ 544 public SparseGradient log1p() { 545 return new SparseGradient(FastMath.log1p(value), 1.0 / (1.0 + value), derivatives); 546 } 547 548 /** {@inheritDoc} */ 549 public SparseGradient cos() { 550 return new SparseGradient(FastMath.cos(value), -FastMath.sin(value), derivatives); 551 } 552 553 /** {@inheritDoc} */ 554 public SparseGradient sin() { 555 return new SparseGradient(FastMath.sin(value), FastMath.cos(value), derivatives); 556 } 557 558 /** {@inheritDoc} */ 559 public SparseGradient tan() { 560 final double t = FastMath.tan(value); 561 return new SparseGradient(t, 1 + t * t, derivatives); 562 } 563 564 /** {@inheritDoc} */ 565 public SparseGradient acos() { 566 return new SparseGradient(FastMath.acos(value), -1.0 / FastMath.sqrt(1 - value * value), derivatives); 567 } 568 569 /** {@inheritDoc} */ 570 public SparseGradient asin() { 571 return new SparseGradient(FastMath.asin(value), 1.0 / FastMath.sqrt(1 - value * value), derivatives); 572 } 573 574 /** {@inheritDoc} */ 575 public SparseGradient atan() { 576 return new SparseGradient(FastMath.atan(value), 1.0 / (1 + value * value), derivatives); 577 } 578 579 /** {@inheritDoc} */ 580 public SparseGradient atan2(final SparseGradient x) { 581 582 // compute r = sqrt(x^2+y^2) 583 final SparseGradient r = multiply(this).add(x.multiply(x)).sqrt(); 584 585 final SparseGradient a; 586 if (x.value >= 0) { 587 588 // compute atan2(y, x) = 2 atan(y / (r + x)) 589 a = divide(r.add(x)).atan().multiply(2); 590 591 } else { 592 593 // compute atan2(y, x) = +/- pi - 2 atan(y / (r - x)) 594 final SparseGradient tmp = divide(r.subtract(x)).atan().multiply(-2); 595 a = tmp.add(tmp.value <= 0 ? -FastMath.PI : FastMath.PI); 596 597 } 598 599 // fix value to take special cases (+0/+0, +0/-0, -0/+0, -0/-0, +/-infinity) correctly 600 a.value = FastMath.atan2(value, x.value); 601 602 return a; 603 604 } 605 606 /** Two arguments arc tangent operation. 607 * @param y first argument of the arc tangent 608 * @param x second argument of the arc tangent 609 * @return atan2(y, x) 610 */ 611 public static SparseGradient atan2(final SparseGradient y, final SparseGradient x) { 612 return y.atan2(x); 613 } 614 615 /** {@inheritDoc} */ 616 public SparseGradient cosh() { 617 return new SparseGradient(FastMath.cosh(value), FastMath.sinh(value), derivatives); 618 } 619 620 /** {@inheritDoc} */ 621 public SparseGradient sinh() { 622 return new SparseGradient(FastMath.sinh(value), FastMath.cosh(value), derivatives); 623 } 624 625 /** {@inheritDoc} */ 626 public SparseGradient tanh() { 627 final double t = FastMath.tanh(value); 628 return new SparseGradient(t, 1 - t * t, derivatives); 629 } 630 631 /** {@inheritDoc} */ 632 public SparseGradient acosh() { 633 return new SparseGradient(FastMath.acosh(value), 1.0 / FastMath.sqrt(value * value - 1.0), derivatives); 634 } 635 636 /** {@inheritDoc} */ 637 public SparseGradient asinh() { 638 return new SparseGradient(FastMath.asinh(value), 1.0 / FastMath.sqrt(value * value + 1.0), derivatives); 639 } 640 641 /** {@inheritDoc} */ 642 public SparseGradient atanh() { 643 return new SparseGradient(FastMath.atanh(value), 1.0 / (1.0 - value * value), derivatives); 644 } 645 646 /** Convert radians to degrees, with error of less than 0.5 ULP 647 * @return instance converted into degrees 648 */ 649 public SparseGradient toDegrees() { 650 return new SparseGradient(FastMath.toDegrees(value), FastMath.toDegrees(1.0), derivatives); 651 } 652 653 /** Convert degrees to radians, with error of less than 0.5 ULP 654 * @return instance converted into radians 655 */ 656 public SparseGradient toRadians() { 657 return new SparseGradient(FastMath.toRadians(value), FastMath.toRadians(1.0), derivatives); 658 } 659 660 /** Evaluate Taylor expansion of a sparse gradient. 661 * @param delta parameters offsets (Δx, Δy, ...) 662 * @return value of the Taylor expansion at x + Δx, y + Δy, ... 663 */ 664 public double taylor(final double ... delta) { 665 double y = value; 666 for (int i = 0; i < delta.length; ++i) { 667 y += delta[i] * getDerivative(i); 668 } 669 return y; 670 } 671 672 /** Compute composition of the instance by a univariate function. 673 * @param f0 value of the function at (i.e. f({@link #getValue()})) 674 * @param f1 first derivative of the function at 675 * the current point (i.e. f'({@link #getValue()})) 676 * @return f(this) 677 */ 678 public SparseGradient compose(final double f0, final double f1) { 679 return new SparseGradient(f0, f1, derivatives); 680 } 681 682 /** {@inheritDoc} */ 683 public SparseGradient linearCombination(final SparseGradient[] a, 684 final SparseGradient[] b) 685 throws DimensionMismatchException { 686 687 // compute a simple value, with all partial derivatives 688 SparseGradient out = a[0].getField().getZero(); 689 for (int i = 0; i < a.length; ++i) { 690 out = out.add(a[i].multiply(b[i])); 691 } 692 693 // recompute an accurate value, taking care of cancellations 694 final double[] aDouble = new double[a.length]; 695 for (int i = 0; i < a.length; ++i) { 696 aDouble[i] = a[i].getValue(); 697 } 698 final double[] bDouble = new double[b.length]; 699 for (int i = 0; i < b.length; ++i) { 700 bDouble[i] = b[i].getValue(); 701 } 702 out.value = MathArrays.linearCombination(aDouble, bDouble); 703 704 return out; 705 706 } 707 708 /** {@inheritDoc} */ 709 public SparseGradient linearCombination(final double[] a, final SparseGradient[] b) { 710 711 // compute a simple value, with all partial derivatives 712 SparseGradient out = b[0].getField().getZero(); 713 for (int i = 0; i < a.length; ++i) { 714 out = out.add(b[i].multiply(a[i])); 715 } 716 717 // recompute an accurate value, taking care of cancellations 718 final double[] bDouble = new double[b.length]; 719 for (int i = 0; i < b.length; ++i) { 720 bDouble[i] = b[i].getValue(); 721 } 722 out.value = MathArrays.linearCombination(a, bDouble); 723 724 return out; 725 726 } 727 728 /** {@inheritDoc} */ 729 public SparseGradient linearCombination(final SparseGradient a1, final SparseGradient b1, 730 final SparseGradient a2, final SparseGradient b2) { 731 732 // compute a simple value, with all partial derivatives 733 SparseGradient out = a1.multiply(b1).add(a2.multiply(b2)); 734 735 // recompute an accurate value, taking care of cancellations 736 out.value = MathArrays.linearCombination(a1.value, b1.value, a2.value, b2.value); 737 738 return out; 739 740 } 741 742 /** {@inheritDoc} */ 743 public SparseGradient linearCombination(final double a1, final SparseGradient b1, 744 final double a2, final SparseGradient b2) { 745 746 // compute a simple value, with all partial derivatives 747 SparseGradient out = b1.multiply(a1).add(b2.multiply(a2)); 748 749 // recompute an accurate value, taking care of cancellations 750 out.value = MathArrays.linearCombination(a1, b1.value, a2, b2.value); 751 752 return out; 753 754 } 755 756 /** {@inheritDoc} */ 757 public SparseGradient linearCombination(final SparseGradient a1, final SparseGradient b1, 758 final SparseGradient a2, final SparseGradient b2, 759 final SparseGradient a3, final SparseGradient b3) { 760 761 // compute a simple value, with all partial derivatives 762 SparseGradient out = a1.multiply(b1).add(a2.multiply(b2)).add(a3.multiply(b3)); 763 764 // recompute an accurate value, taking care of cancellations 765 out.value = MathArrays.linearCombination(a1.value, b1.value, 766 a2.value, b2.value, 767 a3.value, b3.value); 768 769 return out; 770 771 } 772 773 /** {@inheritDoc} */ 774 public SparseGradient linearCombination(final double a1, final SparseGradient b1, 775 final double a2, final SparseGradient b2, 776 final double a3, final SparseGradient b3) { 777 778 // compute a simple value, with all partial derivatives 779 SparseGradient out = b1.multiply(a1).add(b2.multiply(a2)).add(b3.multiply(a3)); 780 781 // recompute an accurate value, taking care of cancellations 782 out.value = MathArrays.linearCombination(a1, b1.value, 783 a2, b2.value, 784 a3, b3.value); 785 786 return out; 787 788 } 789 790 /** {@inheritDoc} */ 791 public SparseGradient linearCombination(final SparseGradient a1, final SparseGradient b1, 792 final SparseGradient a2, final SparseGradient b2, 793 final SparseGradient a3, final SparseGradient b3, 794 final SparseGradient a4, final SparseGradient b4) { 795 796 // compute a simple value, with all partial derivatives 797 SparseGradient out = a1.multiply(b1).add(a2.multiply(b2)).add(a3.multiply(b3)).add(a4.multiply(b4)); 798 799 // recompute an accurate value, taking care of cancellations 800 out.value = MathArrays.linearCombination(a1.value, b1.value, 801 a2.value, b2.value, 802 a3.value, b3.value, 803 a4.value, b4.value); 804 805 return out; 806 807 } 808 809 /** {@inheritDoc} */ 810 public SparseGradient linearCombination(final double a1, final SparseGradient b1, 811 final double a2, final SparseGradient b2, 812 final double a3, final SparseGradient b3, 813 final double a4, final SparseGradient b4) { 814 815 // compute a simple value, with all partial derivatives 816 SparseGradient out = b1.multiply(a1).add(b2.multiply(a2)).add(b3.multiply(a3)).add(b4.multiply(a4)); 817 818 // recompute an accurate value, taking care of cancellations 819 out.value = MathArrays.linearCombination(a1, b1.value, 820 a2, b2.value, 821 a3, b3.value, 822 a4, b4.value); 823 824 return out; 825 826 } 827 828 /** 829 * Test for the equality of two sparse gradients. 830 * <p> 831 * Sparse gradients are considered equal if they have the same value 832 * and the same derivatives. 833 * </p> 834 * @param other Object to test for equality to this 835 * @return true if two sparse gradients are equal 836 */ 837 @Override 838 public boolean equals(Object other) { 839 840 if (this == other) { 841 return true; 842 } 843 844 if (other instanceof SparseGradient) { 845 final SparseGradient rhs = (SparseGradient)other; 846 if (!Precision.equals(value, rhs.value, 1)) { 847 return false; 848 } 849 if (derivatives.size() != rhs.derivatives.size()) { 850 return false; 851 } 852 for (final Map.Entry<Integer, Double> entry : derivatives.entrySet()) { 853 if (!rhs.derivatives.containsKey(entry.getKey())) { 854 return false; 855 } 856 if (!Precision.equals(entry.getValue(), rhs.derivatives.get(entry.getKey()), 1)) { 857 return false; 858 } 859 } 860 return true; 861 } 862 863 return false; 864 865 } 866 867 /** 868 * Get a hashCode for the derivative structure. 869 * @return a hash code value for this object 870 * @since 3.2 871 */ 872 @Override 873 public int hashCode() { 874 return 743 + 809 * MathUtils.hash(value) + 167 * derivatives.hashCode(); 875 } 876 877}