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