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.math.linear; 18 19 import java.io.Serializable; 20 21 import org.apache.commons.math.MathRuntimeException; 22 import org.apache.commons.math.exception.util.LocalizedFormats; 23 import org.apache.commons.math.util.OpenIntToDoubleHashMap; 24 import org.apache.commons.math.util.OpenIntToDoubleHashMap.Iterator; 25 import org.apache.commons.math.util.FastMath; 26 27 /** 28 * This class implements the {@link RealVector} interface with a {@link OpenIntToDoubleHashMap} backing store. 29 * @version $Revision: 1073262 $ $Date: 2011-02-22 10:02:25 +0100 (mar. 22 fvr. 2011) $ 30 * @since 2.0 31 */ 32 public class OpenMapRealVector extends AbstractRealVector implements SparseRealVector, Serializable { 33 34 /** Default Tolerance for having a value considered zero. */ 35 public static final double DEFAULT_ZERO_TOLERANCE = 1.0e-12; 36 37 /** Serializable version identifier. */ 38 private static final long serialVersionUID = 8772222695580707260L; 39 40 /** Entries of the vector. */ 41 private final OpenIntToDoubleHashMap entries; 42 43 /** Dimension of the vector. */ 44 private final int virtualSize; 45 46 /** Tolerance for having a value considered zero. */ 47 private final double epsilon; 48 49 /** 50 * Build a 0-length vector. 51 * <p>Zero-length vectors may be used to initialized construction of vectors 52 * by data gathering. We start with zero-length and use either the {@link 53 * #OpenMapRealVector(OpenMapRealVector, int)} constructor 54 * or one of the <code>append</code> method ({@link #append(double)}, {@link 55 * #append(double[])}, {@link #append(RealVector)}) to gather data 56 * into this vector.</p> 57 */ 58 public OpenMapRealVector() { 59 this(0, DEFAULT_ZERO_TOLERANCE); 60 } 61 62 /** 63 * Construct a (dimension)-length vector of zeros. 64 * @param dimension size of the vector 65 */ 66 public OpenMapRealVector(int dimension) { 67 this(dimension, DEFAULT_ZERO_TOLERANCE); 68 } 69 70 /** 71 * Construct a (dimension)-length vector of zeros, specifying zero tolerance. 72 * @param dimension Size of the vector 73 * @param epsilon The tolerance for having a value considered zero 74 */ 75 public OpenMapRealVector(int dimension, double epsilon) { 76 virtualSize = dimension; 77 entries = new OpenIntToDoubleHashMap(0.0); 78 this.epsilon = epsilon; 79 } 80 81 /** 82 * Build a resized vector, for use with append. 83 * @param v The original vector 84 * @param resize The amount to resize it 85 */ 86 protected OpenMapRealVector(OpenMapRealVector v, int resize) { 87 virtualSize = v.getDimension() + resize; 88 entries = new OpenIntToDoubleHashMap(v.entries); 89 epsilon = v.epsilon; 90 } 91 92 /** 93 * Build a vector with known the sparseness (for advanced use only). 94 * @param dimension The size of the vector 95 * @param expectedSize The expected number of non-zero entries 96 */ 97 public OpenMapRealVector(int dimension, int expectedSize) { 98 this(dimension, expectedSize, DEFAULT_ZERO_TOLERANCE); 99 } 100 101 /** 102 * Build a vector with known the sparseness and zero tolerance setting (for advanced use only). 103 * @param dimension The size of the vector 104 * @param expectedSize The expected number of non-zero entries 105 * @param epsilon The tolerance for having a value considered zero 106 */ 107 public OpenMapRealVector(int dimension, int expectedSize, double epsilon) { 108 virtualSize = dimension; 109 entries = new OpenIntToDoubleHashMap(expectedSize, 0.0); 110 this.epsilon = epsilon; 111 } 112 113 /** 114 * Create from a double array. 115 * Only non-zero entries will be stored 116 * @param values The set of values to create from 117 */ 118 public OpenMapRealVector(double[] values) { 119 this(values, DEFAULT_ZERO_TOLERANCE); 120 } 121 122 /** 123 * Create from a double array, specifying zero tolerance. 124 * Only non-zero entries will be stored 125 * @param values The set of values to create from 126 * @param epsilon The tolerance for having a value considered zero 127 */ 128 public OpenMapRealVector(double[] values, double epsilon) { 129 virtualSize = values.length; 130 entries = new OpenIntToDoubleHashMap(0.0); 131 this.epsilon = epsilon; 132 for (int key = 0; key < values.length; key++) { 133 double value = values[key]; 134 if (!isDefaultValue(value)) { 135 entries.put(key, value); 136 } 137 } 138 } 139 140 /** 141 * Create from a Double array. 142 * Only non-zero entries will be stored 143 * @param values The set of values to create from 144 */ 145 public OpenMapRealVector(Double[] values) { 146 this(values, DEFAULT_ZERO_TOLERANCE); 147 } 148 149 /** 150 * Create from a Double array. 151 * Only non-zero entries will be stored 152 * @param values The set of values to create from 153 * @param epsilon The tolerance for having a value considered zero 154 */ 155 public OpenMapRealVector(Double[] values, double epsilon) { 156 virtualSize = values.length; 157 entries = new OpenIntToDoubleHashMap(0.0); 158 this.epsilon = epsilon; 159 for (int key = 0; key < values.length; key++) { 160 double value = values[key].doubleValue(); 161 if (!isDefaultValue(value)) { 162 entries.put(key, value); 163 } 164 } 165 } 166 167 /** 168 * Copy constructor. 169 * @param v The instance to copy from 170 */ 171 public OpenMapRealVector(OpenMapRealVector v) { 172 virtualSize = v.getDimension(); 173 entries = new OpenIntToDoubleHashMap(v.getEntries()); 174 epsilon = v.epsilon; 175 } 176 177 /** 178 * Generic copy constructor. 179 * @param v The instance to copy from 180 */ 181 public OpenMapRealVector(RealVector v) { 182 virtualSize = v.getDimension(); 183 entries = new OpenIntToDoubleHashMap(0.0); 184 epsilon = DEFAULT_ZERO_TOLERANCE; 185 for (int key = 0; key < virtualSize; key++) { 186 double value = v.getEntry(key); 187 if (!isDefaultValue(value)) { 188 entries.put(key, value); 189 } 190 } 191 } 192 193 /** 194 * Get the entries of this instance. 195 * @return entries of this instance 196 */ 197 private OpenIntToDoubleHashMap getEntries() { 198 return entries; 199 } 200 201 /** 202 * Determine if this value is within epsilon of zero. 203 * @param value The value to test 204 * @return <code>true</code> if this value is within epsilon to zero, <code>false</code> otherwise 205 * @since 2.1 206 */ 207 protected boolean isDefaultValue(double value) { 208 return FastMath.abs(value) < epsilon; 209 } 210 211 /** {@inheritDoc} */ 212 @Override 213 public RealVector add(RealVector v) throws IllegalArgumentException { 214 checkVectorDimensions(v.getDimension()); 215 if (v instanceof OpenMapRealVector) { 216 return add((OpenMapRealVector) v); 217 } else { 218 return super.add(v); 219 } 220 } 221 222 /** 223 * Optimized method to add two OpenMapRealVectors. Copies the larger vector, iterates over the smaller. 224 * @param v Vector to add with 225 * @return The sum of <code>this</code> with <code>v</code> 226 * @throws IllegalArgumentException If the dimensions don't match 227 */ 228 public OpenMapRealVector add(OpenMapRealVector v) throws IllegalArgumentException{ 229 checkVectorDimensions(v.getDimension()); 230 boolean copyThis = entries.size() > v.entries.size(); 231 OpenMapRealVector res = copyThis ? this.copy() : v.copy(); 232 Iterator iter = copyThis ? v.entries.iterator() : entries.iterator(); 233 OpenIntToDoubleHashMap randomAccess = copyThis ? entries : v.entries; 234 while (iter.hasNext()) { 235 iter.advance(); 236 int key = iter.key(); 237 if (randomAccess.containsKey(key)) { 238 res.setEntry(key, randomAccess.get(key) + iter.value()); 239 } else { 240 res.setEntry(key, iter.value()); 241 } 242 } 243 return res; 244 } 245 246 /** 247 * Optimized method to append a OpenMapRealVector. 248 * @param v vector to append 249 * @return The result of appending <code>v</code> to self 250 */ 251 public OpenMapRealVector append(OpenMapRealVector v) { 252 OpenMapRealVector res = new OpenMapRealVector(this, v.getDimension()); 253 Iterator iter = v.entries.iterator(); 254 while (iter.hasNext()) { 255 iter.advance(); 256 res.setEntry(iter.key() + virtualSize, iter.value()); 257 } 258 return res; 259 } 260 261 /** {@inheritDoc} */ 262 public OpenMapRealVector append(RealVector v) { 263 if (v instanceof OpenMapRealVector) { 264 return append((OpenMapRealVector) v); 265 } 266 return append(v.getData()); 267 } 268 269 /** {@inheritDoc} */ 270 public OpenMapRealVector append(double d) { 271 OpenMapRealVector res = new OpenMapRealVector(this, 1); 272 res.setEntry(virtualSize, d); 273 return res; 274 } 275 276 /** {@inheritDoc} */ 277 public OpenMapRealVector append(double[] a) { 278 OpenMapRealVector res = new OpenMapRealVector(this, a.length); 279 for (int i = 0; i < a.length; i++) { 280 res.setEntry(i + virtualSize, a[i]); 281 } 282 return res; 283 } 284 285 /** 286 * {@inheritDoc} 287 * @since 2.1 288 */ 289 @Override 290 public OpenMapRealVector copy() { 291 return new OpenMapRealVector(this); 292 } 293 294 /** 295 * Optimized method to compute the dot product with an OpenMapRealVector. 296 * Iterates over the smaller of the two. 297 * @param v The vector to compute the dot product with 298 * @return The dot product of <code>this</code> and <code>v</code> 299 * @throws IllegalArgumentException If the dimensions don't match 300 */ 301 public double dotProduct(OpenMapRealVector v) throws IllegalArgumentException { 302 checkVectorDimensions(v.getDimension()); 303 boolean thisIsSmaller = entries.size() < v.entries.size(); 304 Iterator iter = thisIsSmaller ? entries.iterator() : v.entries.iterator(); 305 OpenIntToDoubleHashMap larger = thisIsSmaller ? v.entries : entries; 306 double d = 0; 307 while(iter.hasNext()) { 308 iter.advance(); 309 d += iter.value() * larger.get(iter.key()); 310 } 311 return d; 312 } 313 314 /** {@inheritDoc} */ 315 @Override 316 public double dotProduct(RealVector v) throws IllegalArgumentException { 317 if(v instanceof OpenMapRealVector) { 318 return dotProduct((OpenMapRealVector)v); 319 } else { 320 return super.dotProduct(v); 321 } 322 } 323 324 /** {@inheritDoc} */ 325 public OpenMapRealVector ebeDivide(RealVector v) throws IllegalArgumentException { 326 checkVectorDimensions(v.getDimension()); 327 OpenMapRealVector res = new OpenMapRealVector(this); 328 Iterator iter = res.entries.iterator(); 329 while (iter.hasNext()) { 330 iter.advance(); 331 res.setEntry(iter.key(), iter.value() / v.getEntry(iter.key())); 332 } 333 return res; 334 } 335 336 /** {@inheritDoc} */ 337 @Override 338 public OpenMapRealVector ebeDivide(double[] v) throws IllegalArgumentException { 339 checkVectorDimensions(v.length); 340 OpenMapRealVector res = new OpenMapRealVector(this); 341 Iterator iter = res.entries.iterator(); 342 while (iter.hasNext()) { 343 iter.advance(); 344 res.setEntry(iter.key(), iter.value() / v[iter.key()]); 345 } 346 return res; 347 } 348 349 /** {@inheritDoc} */ 350 public OpenMapRealVector ebeMultiply(RealVector v) throws IllegalArgumentException { 351 checkVectorDimensions(v.getDimension()); 352 OpenMapRealVector res = new OpenMapRealVector(this); 353 Iterator iter = res.entries.iterator(); 354 while (iter.hasNext()) { 355 iter.advance(); 356 res.setEntry(iter.key(), iter.value() * v.getEntry(iter.key())); 357 } 358 return res; 359 } 360 361 /** {@inheritDoc} */ 362 @Override 363 public OpenMapRealVector ebeMultiply(double[] v) throws IllegalArgumentException { 364 checkVectorDimensions(v.length); 365 OpenMapRealVector res = new OpenMapRealVector(this); 366 Iterator iter = res.entries.iterator(); 367 while (iter.hasNext()) { 368 iter.advance(); 369 res.setEntry(iter.key(), iter.value() * v[iter.key()]); 370 } 371 return res; 372 } 373 374 /** {@inheritDoc} */ 375 public OpenMapRealVector getSubVector(int index, int n) throws MatrixIndexException { 376 checkIndex(index); 377 checkIndex(index + n - 1); 378 OpenMapRealVector res = new OpenMapRealVector(n); 379 int end = index + n; 380 Iterator iter = entries.iterator(); 381 while (iter.hasNext()) { 382 iter.advance(); 383 int key = iter.key(); 384 if (key >= index && key < end) { 385 res.setEntry(key - index, iter.value()); 386 } 387 } 388 return res; 389 } 390 391 /** {@inheritDoc} */ 392 @Override 393 public double[] getData() { 394 double[] res = new double[virtualSize]; 395 Iterator iter = entries.iterator(); 396 while (iter.hasNext()) { 397 iter.advance(); 398 res[iter.key()] = iter.value(); 399 } 400 return res; 401 } 402 403 /** {@inheritDoc} */ 404 public int getDimension() { 405 return virtualSize; 406 } 407 408 /** 409 * Optimized method to compute distance. 410 * @param v The vector to compute distance to 411 * @return The distance from <code>this</code> and <code>v</code> 412 * @throws IllegalArgumentException If the dimensions don't match 413 */ 414 public double getDistance(OpenMapRealVector v) throws IllegalArgumentException { 415 Iterator iter = entries.iterator(); 416 double res = 0; 417 while (iter.hasNext()) { 418 iter.advance(); 419 int key = iter.key(); 420 double delta; 421 delta = iter.value() - v.getEntry(key); 422 res += delta * delta; 423 } 424 iter = v.getEntries().iterator(); 425 while (iter.hasNext()) { 426 iter.advance(); 427 int key = iter.key(); 428 if (!entries.containsKey(key)) { 429 final double value = iter.value(); 430 res += value * value; 431 } 432 } 433 return FastMath.sqrt(res); 434 } 435 436 /** {@inheritDoc} */ 437 @Override 438 public double getDistance(RealVector v) throws IllegalArgumentException { 439 checkVectorDimensions(v.getDimension()); 440 if (v instanceof OpenMapRealVector) { 441 return getDistance((OpenMapRealVector) v); 442 } 443 return getDistance(v.getData()); 444 } 445 446 /** {@inheritDoc} */ 447 @Override 448 public double getDistance(double[] v) throws IllegalArgumentException { 449 checkVectorDimensions(v.length); 450 double res = 0; 451 for (int i = 0; i < v.length; i++) { 452 double delta = entries.get(i) - v[i]; 453 res += delta * delta; 454 } 455 return FastMath.sqrt(res); 456 } 457 458 /** {@inheritDoc} */ 459 public double getEntry(int index) throws MatrixIndexException { 460 checkIndex(index); 461 return entries.get(index); 462 } 463 464 /** 465 * Distance between two vectors. 466 * <p>This method computes the distance consistent with 467 * L<sub>1</sub> norm, i.e. the sum of the absolute values of 468 * elements differences.</p> 469 * @param v vector to which distance is requested 470 * @return distance between two vectors. 471 */ 472 public double getL1Distance(OpenMapRealVector v) { 473 double max = 0; 474 Iterator iter = entries.iterator(); 475 while (iter.hasNext()) { 476 iter.advance(); 477 double delta = FastMath.abs(iter.value() - v.getEntry(iter.key())); 478 max += delta; 479 } 480 iter = v.getEntries().iterator(); 481 while (iter.hasNext()) { 482 iter.advance(); 483 int key = iter.key(); 484 if (!entries.containsKey(key)) { 485 double delta = FastMath.abs(iter.value()); 486 max += FastMath.abs(delta); 487 } 488 } 489 return max; 490 } 491 492 /** {@inheritDoc} */ 493 @Override 494 public double getL1Distance(RealVector v) throws IllegalArgumentException { 495 checkVectorDimensions(v.getDimension()); 496 if (v instanceof OpenMapRealVector) { 497 return getL1Distance((OpenMapRealVector) v); 498 } 499 return getL1Distance(v.getData()); 500 } 501 502 /** {@inheritDoc} */ 503 @Override 504 public double getL1Distance(double[] v) throws IllegalArgumentException { 505 checkVectorDimensions(v.length); 506 double max = 0; 507 for (int i = 0; i < v.length; i++) { 508 double delta = FastMath.abs(getEntry(i) - v[i]); 509 max += delta; 510 } 511 return max; 512 } 513 514 /** 515 * Optimized method to compute LInfDistance. 516 * @param v The vector to compute from 517 * @return the LInfDistance 518 */ 519 private double getLInfDistance(OpenMapRealVector v) { 520 double max = 0; 521 Iterator iter = entries.iterator(); 522 while (iter.hasNext()) { 523 iter.advance(); 524 double delta = FastMath.abs(iter.value() - v.getEntry(iter.key())); 525 if (delta > max) { 526 max = delta; 527 } 528 } 529 iter = v.getEntries().iterator(); 530 while (iter.hasNext()) { 531 iter.advance(); 532 int key = iter.key(); 533 if (!entries.containsKey(key)) { 534 if (iter.value() > max) { 535 max = iter.value(); 536 } 537 } 538 } 539 return max; 540 } 541 542 /** {@inheritDoc} */ 543 @Override 544 public double getLInfDistance(RealVector v) throws IllegalArgumentException { 545 checkVectorDimensions(v.getDimension()); 546 if (v instanceof OpenMapRealVector) { 547 return getLInfDistance((OpenMapRealVector) v); 548 } 549 return getLInfDistance(v.getData()); 550 } 551 552 /** {@inheritDoc} */ 553 @Override 554 public double getLInfDistance(double[] v) throws IllegalArgumentException { 555 checkVectorDimensions(v.length); 556 double max = 0; 557 for (int i = 0; i < v.length; i++) { 558 double delta = FastMath.abs(getEntry(i) - v[i]); 559 if (delta > max) { 560 max = delta; 561 } 562 } 563 return max; 564 } 565 566 /** {@inheritDoc} */ 567 public boolean isInfinite() { 568 boolean infiniteFound = false; 569 Iterator iter = entries.iterator(); 570 while (iter.hasNext()) { 571 iter.advance(); 572 final double value = iter.value(); 573 if (Double.isNaN(value)) { 574 return false; 575 } 576 if (Double.isInfinite(value)) { 577 infiniteFound = true; 578 } 579 } 580 return infiniteFound; 581 } 582 583 /** {@inheritDoc} */ 584 public boolean isNaN() { 585 Iterator iter = entries.iterator(); 586 while (iter.hasNext()) { 587 iter.advance(); 588 if (Double.isNaN(iter.value())) { 589 return true; 590 } 591 } 592 return false; 593 } 594 595 /** {@inheritDoc} */ 596 @Override 597 public OpenMapRealVector mapAdd(double d) { 598 return copy().mapAddToSelf(d); 599 } 600 601 /** {@inheritDoc} */ 602 @Override 603 public OpenMapRealVector mapAddToSelf(double d) { 604 for (int i = 0; i < virtualSize; i++) { 605 setEntry(i, getEntry(i) + d); 606 } 607 return this; 608 } 609 610 /** {@inheritDoc} */ 611 @Override 612 public RealMatrix outerProduct(double[] v) throws IllegalArgumentException { 613 checkVectorDimensions(v.length); 614 RealMatrix res = new OpenMapRealMatrix(virtualSize, virtualSize); 615 Iterator iter = entries.iterator(); 616 while (iter.hasNext()) { 617 iter.advance(); 618 int row = iter.key(); 619 double value = iter.value(); 620 for (int col = 0; col < virtualSize; col++) { 621 res.setEntry(row, col, value * v[col]); 622 } 623 } 624 return res; 625 } 626 627 /** {@inheritDoc} */ 628 public RealVector projection(RealVector v) throws IllegalArgumentException { 629 checkVectorDimensions(v.getDimension()); 630 return v.mapMultiply(dotProduct(v) / v.dotProduct(v)); 631 } 632 633 /** {@inheritDoc} */ 634 @Override 635 public OpenMapRealVector projection(double[] v) throws IllegalArgumentException { 636 checkVectorDimensions(v.length); 637 return (OpenMapRealVector) projection(new OpenMapRealVector(v)); 638 } 639 640 /** {@inheritDoc} */ 641 public void setEntry(int index, double value) throws MatrixIndexException { 642 checkIndex(index); 643 if (!isDefaultValue(value)) { 644 entries.put(index, value); 645 } else if (entries.containsKey(index)) { 646 entries.remove(index); 647 } 648 } 649 650 /** {@inheritDoc} */ 651 @Override 652 public void setSubVector(int index, RealVector v) throws MatrixIndexException { 653 checkIndex(index); 654 checkIndex(index + v.getDimension() - 1); 655 setSubVector(index, v.getData()); 656 } 657 658 /** {@inheritDoc} */ 659 @Override 660 public void setSubVector(int index, double[] v) throws MatrixIndexException { 661 checkIndex(index); 662 checkIndex(index + v.length - 1); 663 for (int i = 0; i < v.length; i++) { 664 setEntry(i + index, v[i]); 665 } 666 } 667 668 /** {@inheritDoc} */ 669 @Override 670 public void set(double value) { 671 for (int i = 0; i < virtualSize; i++) { 672 setEntry(i, value); 673 } 674 } 675 676 /** 677 * Optimized method to subtract OpenMapRealVectors. 678 * @param v The vector to subtract from <code>this</code> 679 * @return The difference of <code>this</code> and <code>v</code> 680 * @throws IllegalArgumentException If the dimensions don't match 681 */ 682 public OpenMapRealVector subtract(OpenMapRealVector v) throws IllegalArgumentException{ 683 checkVectorDimensions(v.getDimension()); 684 OpenMapRealVector res = copy(); 685 Iterator iter = v.getEntries().iterator(); 686 while (iter.hasNext()) { 687 iter.advance(); 688 int key = iter.key(); 689 if (entries.containsKey(key)) { 690 res.setEntry(key, entries.get(key) - iter.value()); 691 } else { 692 res.setEntry(key, -iter.value()); 693 } 694 } 695 return res; 696 } 697 698 /** {@inheritDoc} */ 699 @Override 700 public OpenMapRealVector subtract(RealVector v) throws IllegalArgumentException { 701 checkVectorDimensions(v.getDimension()); 702 if (v instanceof OpenMapRealVector) { 703 return subtract((OpenMapRealVector) v); 704 } 705 return subtract(v.getData()); 706 } 707 708 /** {@inheritDoc} */ 709 @Override 710 public OpenMapRealVector subtract(double[] v) throws IllegalArgumentException { 711 checkVectorDimensions(v.length); 712 OpenMapRealVector res = new OpenMapRealVector(this); 713 for (int i = 0; i < v.length; i++) { 714 if (entries.containsKey(i)) { 715 res.setEntry(i, entries.get(i) - v[i]); 716 } else { 717 res.setEntry(i, -v[i]); 718 } 719 } 720 return res; 721 } 722 723 724 /** {@inheritDoc} */ 725 @Override 726 public OpenMapRealVector unitVector() { 727 OpenMapRealVector res = copy(); 728 res.unitize(); 729 return res; 730 } 731 732 /** {@inheritDoc} */ 733 @Override 734 public void unitize() { 735 double norm = getNorm(); 736 if (isDefaultValue(norm)) { 737 throw MathRuntimeException.createArithmeticException(LocalizedFormats.CANNOT_NORMALIZE_A_ZERO_NORM_VECTOR); 738 } 739 Iterator iter = entries.iterator(); 740 while (iter.hasNext()) { 741 iter.advance(); 742 entries.put(iter.key(), iter.value() / norm); 743 } 744 745 } 746 747 748 /** {@inheritDoc} */ 749 @Override 750 public double[] toArray() { 751 return getData(); 752 } 753 754 /** {@inheritDoc} 755 * <p> Implementation Note: This works on exact values, and as a result 756 * it is possible for {@code a.subtract(b)} to be the zero vector, while 757 * {@code a.hashCode() != b.hashCode()}.</p> 758 */ 759 @Override 760 public int hashCode() { 761 final int prime = 31; 762 int result = 1; 763 long temp; 764 temp = Double.doubleToLongBits(epsilon); 765 result = prime * result + (int) (temp ^ (temp >>> 32)); 766 result = prime * result + virtualSize; 767 Iterator iter = entries.iterator(); 768 while (iter.hasNext()) { 769 iter.advance(); 770 temp = Double.doubleToLongBits(iter.value()); 771 result = prime * result + (int) (temp ^ (temp >>32)); 772 } 773 return result; 774 } 775 776 /** 777 * <p> Implementation Note: This performs an exact comparison, and as a result 778 * it is possible for {@code a.subtract(b}} to be the zero vector, while 779 * {@code a.equals(b) == false}.</p> 780 * {@inheritDoc} 781 */ 782 @Override 783 public boolean equals(Object obj) { 784 if (this == obj) { 785 return true; 786 } 787 if (!(obj instanceof OpenMapRealVector)) { 788 return false; 789 } 790 OpenMapRealVector other = (OpenMapRealVector) obj; 791 if (virtualSize != other.virtualSize) { 792 return false; 793 } 794 if (Double.doubleToLongBits(epsilon) != 795 Double.doubleToLongBits(other.epsilon)) { 796 return false; 797 } 798 Iterator iter = entries.iterator(); 799 while (iter.hasNext()) { 800 iter.advance(); 801 double test = other.getEntry(iter.key()); 802 if (Double.doubleToLongBits(test) != Double.doubleToLongBits(iter.value())) { 803 return false; 804 } 805 } 806 iter = other.getEntries().iterator(); 807 while (iter.hasNext()) { 808 iter.advance(); 809 double test = iter.value(); 810 if (Double.doubleToLongBits(test) != Double.doubleToLongBits(getEntry(iter.key()))) { 811 return false; 812 } 813 } 814 return true; 815 } 816 817 /** 818 * 819 * @return the percentage of none zero elements as a decimal percent. 820 * @deprecated as of 2.2 replaced by the correctly spelled {@link #getSparsity()} 821 */ 822 @Deprecated 823 public double getSparcity() { 824 return getSparsity(); 825 } 826 827 /** 828 * 829 * @return the percentage of none zero elements as a decimal percent. 830 * @since 2.2 831 */ 832 public double getSparsity() { 833 return (double)entries.size()/(double)getDimension(); 834 } 835 836 /** {@inheritDoc} */ 837 @Override 838 public java.util.Iterator<Entry> sparseIterator() { 839 return new OpenMapSparseIterator(); 840 } 841 842 /** 843 * Implementation of <code>Entry</code> optimized for OpenMap. 844 * <p>This implementation does not allow arbitrary calls to <code>setIndex</code> 845 * since the order that entries are returned is undefined. 846 */ 847 protected class OpenMapEntry extends Entry { 848 849 /** Iterator pointing to the entry. */ 850 private final Iterator iter; 851 852 /** Build an entry from an iterator point to an element. 853 * @param iter iterator pointing to the entry 854 */ 855 protected OpenMapEntry(Iterator iter) { 856 this.iter = iter; 857 } 858 859 /** {@inheritDoc} */ 860 @Override 861 public double getValue() { 862 return iter.value(); 863 } 864 865 /** {@inheritDoc} */ 866 @Override 867 public void setValue(double value) { 868 entries.put(iter.key(), value); 869 } 870 871 /** {@inheritDoc} */ 872 @Override 873 public int getIndex() { 874 return iter.key(); 875 } 876 877 } 878 879 /** 880 * Iterator class to do iteration over just the non-zero elements. 881 * <p>This implementation is fail-fast, so cannot be used to modify any zero element. 882 * 883 */ 884 protected class OpenMapSparseIterator implements java.util.Iterator<Entry> { 885 886 /** Underlying iterator. */ 887 private final Iterator iter; 888 889 /** Current entry. */ 890 private final Entry current; 891 892 /** Simple constructor. */ 893 protected OpenMapSparseIterator() { 894 iter = entries.iterator(); 895 current = new OpenMapEntry(iter); 896 } 897 898 /** {@inheritDoc} */ 899 public boolean hasNext() { 900 return iter.hasNext(); 901 } 902 903 /** {@inheritDoc} */ 904 public Entry next() { 905 iter.advance(); 906 return current; 907 } 908 909 /** {@inheritDoc} */ 910 public void remove() { 911 throw new UnsupportedOperationException("Not supported"); 912 } 913 914 } 915 } 916