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 }