    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.exception.util.LocalizedFormats;
    023    import org.apache.commons.math.util.OpenIntToDoubleHashMap;
    024    import org.apache.commons.math.util.OpenIntToDoubleHashMap.Iterator;
    025    import org.apache.commons.math.util.FastMath;
    026
    027    /**
    028     * This class implements the {@link RealVector} interface with a {@link OpenIntToDoubleHashMap} backing store.
    029     * @version $Revision: 1073262 $ $Date: 2011-02-22 10:02:25 +0100 (mar. 22 f??vr. 2011) $
    030     * @since 2.0
    031    */
    032    public class OpenMapRealVector extends AbstractRealVector implements SparseRealVector, Serializable {
    033
    034        /** Default Tolerance for having a value considered zero. */
    035        public static final double DEFAULT_ZERO_TOLERANCE = 1.0e-12;
    036
    037        /** Serializable version identifier. */
    038        private static final long serialVersionUID = 8772222695580707260L;
    039
    040        /** Entries of the vector. */
    041        private final OpenIntToDoubleHashMap entries;
    042
    043        /** Dimension of the vector. */
    044        private final int virtualSize;
    045
    046        /** Tolerance for having a value considered zero. */
    047        private final double epsilon;
    048
    049        /**
    050         * Build a 0-length vector.
    051         * <p>Zero-length vectors may be used to initialized construction of vectors
    052         * by data gathering. We start with zero-length and use either the {@link
    053         * #OpenMapRealVector(OpenMapRealVector, int)} constructor
    054         * or one of the <code>append</code> method ({@link #append(double)}, {@link
    055         * #append(double[])}, {@link #append(RealVector)}) to gather data
    056         * into this vector.</p>
    057         */
    058        public OpenMapRealVector() {
    059            this(0, DEFAULT_ZERO_TOLERANCE);
    060        }
    061
    062        /**
    063         * Construct a (dimension)-length vector of zeros.
    064         * @param dimension size of the vector
    065         */
    066        public OpenMapRealVector(int dimension) {
    067            this(dimension, DEFAULT_ZERO_TOLERANCE);
    068        }
    069
    070        /**
    071         * Construct a (dimension)-length vector of zeros, specifying zero tolerance.
    072         * @param dimension Size of the vector
    073         * @param epsilon The tolerance for having a value considered zero
    074         */
    075        public OpenMapRealVector(int dimension, double epsilon) {
    076            virtualSize = dimension;
    077            entries = new OpenIntToDoubleHashMap(0.0);
    078            this.epsilon = epsilon;
    079        }
    080
    081        /**
    082         * Build a resized vector, for use with append.
    083         * @param v The original vector
    084         * @param resize The amount to resize it
    085         */
    086        protected OpenMapRealVector(OpenMapRealVector v, int resize) {
    087            virtualSize = v.getDimension() + resize;
    088            entries = new OpenIntToDoubleHashMap(v.entries);
    089            epsilon = v.epsilon;
    090        }
    091
    092        /**
    093         * Build a vector with known the sparseness (for advanced use only).
    094         * @param dimension The size of the vector
    095         * @param expectedSize The expected number of non-zero entries
    096         */
    097        public OpenMapRealVector(int dimension, int expectedSize) {
    098            this(dimension, expectedSize, DEFAULT_ZERO_TOLERANCE);
    099        }
    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    }