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