Home | History | Annotate | Download | only in linear
      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