OpenMapRealVector.java

  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.  *      https://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. /*
  18.  * This is not the original file distributed by the Apache Software Foundation
  19.  * It has been modified by the Hipparchus project
  20.  */
  21. package org.hipparchus.linear;

  22. import java.io.Serializable;

  23. import org.hipparchus.exception.LocalizedCoreFormats;
  24. import org.hipparchus.exception.MathIllegalArgumentException;
  25. import org.hipparchus.exception.MathRuntimeException;
  26. import org.hipparchus.util.FastMath;
  27. import org.hipparchus.util.OpenIntToDoubleHashMap;
  28. import org.hipparchus.util.OpenIntToDoubleHashMap.Iterator;

  29. /**
  30.  * This class implements the {@link RealVector} interface with a
  31.  * {@link OpenIntToDoubleHashMap} backing store.
  32.  * <p>
  33.  *  Caveat: This implementation assumes that, for any {@code x},
  34.  *  the equality {@code x * 0d == 0d} holds. But it is is not true for
  35.  *  {@code NaN}. Moreover, zero entries will lose their sign.
  36.  *  Some operations (that involve {@code NaN} and/or infinities) may
  37.  *  thus give incorrect results, like multiplications, divisions or
  38.  *  functions mapping.
  39.  * </p>
  40.  */
  41. public class OpenMapRealVector extends SparseRealVector
  42.     implements Serializable {
  43.     /** Default Tolerance for having a value considered zero. */
  44.     public static final double DEFAULT_ZERO_TOLERANCE = 1.0e-12;
  45.     /** Serializable version identifier. */
  46.     private static final long serialVersionUID = 8772222695580707260L;
  47.     /** Entries of the vector. */
  48.     private final OpenIntToDoubleHashMap entries;
  49.     /** Dimension of the vector. */
  50.     private final int virtualSize;
  51.     /** Tolerance for having a value considered zero. */
  52.     private final double epsilon;

  53.     /**
  54.      * Build a 0-length vector.
  55.      * Zero-length vectors may be used to initialized construction of vectors
  56.      * by data gathering. We start with zero-length and use either the {@link
  57.      * #OpenMapRealVector(OpenMapRealVector, int)} constructor
  58.      * or one of the {@code append} method ({@link #append(double)},
  59.      * {@link #append(RealVector)}) to gather data into this vector.
  60.      */
  61.     public OpenMapRealVector() {
  62.         this(0, DEFAULT_ZERO_TOLERANCE);
  63.     }

  64.     /**
  65.      * Construct a vector of zeroes.
  66.      *
  67.      * @param dimension Size of the vector.
  68.      */
  69.     public OpenMapRealVector(int dimension) {
  70.         this(dimension, DEFAULT_ZERO_TOLERANCE);
  71.     }

  72.     /**
  73.      * Construct a vector of zeroes, specifying zero tolerance.
  74.      *
  75.      * @param dimension Size of the vector.
  76.      * @param epsilon Tolerance below which a value considered zero.
  77.      */
  78.     public OpenMapRealVector(int dimension, double epsilon) {
  79.         virtualSize = dimension;
  80.         entries = new OpenIntToDoubleHashMap(0.0);
  81.         this.epsilon = epsilon;
  82.     }

  83.     /**
  84.      * Build a resized vector, for use with append.
  85.      *
  86.      * @param v Original vector.
  87.      * @param resize Amount to add.
  88.      */
  89.     protected OpenMapRealVector(OpenMapRealVector v, int resize) {
  90.         virtualSize = v.getDimension() + resize;
  91.         entries = new OpenIntToDoubleHashMap(v.entries);
  92.         epsilon = v.epsilon;
  93.     }

  94.     /**
  95.      * Build a vector with known the sparseness (for advanced use only).
  96.      *
  97.      * @param dimension Size of the vector.
  98.      * @param expectedSize The expected number of non-zero entries.
  99.      */
  100.     public OpenMapRealVector(int dimension, int expectedSize) {
  101.         this(dimension, expectedSize, DEFAULT_ZERO_TOLERANCE);
  102.     }

  103.     /**
  104.      * Build a vector with known the sparseness and zero tolerance
  105.      * setting (for advanced use only).
  106.      *
  107.      * @param dimension Size of the vector.
  108.      * @param expectedSize Expected number of non-zero entries.
  109.      * @param epsilon Tolerance below which a value is considered zero.
  110.      */
  111.     public OpenMapRealVector(int dimension, int expectedSize, double epsilon) {
  112.         virtualSize = dimension;
  113.         entries = new OpenIntToDoubleHashMap(expectedSize, 0.0);
  114.         this.epsilon = epsilon;
  115.     }

  116.     /**
  117.      * Create from an array.
  118.      * Only non-zero entries will be stored.
  119.      *
  120.      * @param values Set of values to create from.
  121.      */
  122.     public OpenMapRealVector(double[] values) {
  123.         this(values, DEFAULT_ZERO_TOLERANCE);
  124.     }

  125.     /**
  126.      * Create from an array, specifying zero tolerance.
  127.      * Only non-zero entries will be stored.
  128.      *
  129.      * @param values Set of values to create from.
  130.      * @param epsilon Tolerance below which a value is considered zero.
  131.      */
  132.     public OpenMapRealVector(double[] values, double epsilon) {
  133.         virtualSize = values.length;
  134.         entries = new OpenIntToDoubleHashMap(0.0);
  135.         this.epsilon = epsilon;
  136.         for (int key = 0; key < values.length; key++) {
  137.             double value = values[key];
  138.             if (!isDefaultValue(value)) {
  139.                 entries.put(key, value);
  140.             }
  141.         }
  142.     }

  143.     /**
  144.      * Create from an array.
  145.      * Only non-zero entries will be stored.
  146.      *
  147.      * @param values The set of values to create from
  148.      */
  149.     public OpenMapRealVector(Double[] values) {
  150.         this(values, DEFAULT_ZERO_TOLERANCE);
  151.     }

  152.     /**
  153.      * Create from an array.
  154.      * Only non-zero entries will be stored.
  155.      *
  156.      * @param values Set of values to create from.
  157.      * @param epsilon Tolerance below which a value is considered zero.
  158.      */
  159.     public OpenMapRealVector(Double[] values, double epsilon) {
  160.         virtualSize = values.length;
  161.         entries = new OpenIntToDoubleHashMap(0.0);
  162.         this.epsilon = epsilon;
  163.         for (int key = 0; key < values.length; key++) {
  164.             double value = values[key];
  165.             if (!isDefaultValue(value)) {
  166.                 entries.put(key, value);
  167.             }
  168.         }
  169.     }

  170.     /**
  171.      * Copy constructor.
  172.      *
  173.      * @param v Instance to copy from.
  174.      */
  175.     public OpenMapRealVector(OpenMapRealVector v) {
  176.         virtualSize = v.getDimension();
  177.         entries = new OpenIntToDoubleHashMap(v.getEntries());
  178.         epsilon = v.epsilon;
  179.     }

  180.     /**
  181.      * Generic copy constructor.
  182.      *
  183.      * @param v Instance to copy from.
  184.      */
  185.     public OpenMapRealVector(RealVector v) {
  186.         virtualSize = v.getDimension();
  187.         entries = new OpenIntToDoubleHashMap(0.0);
  188.         epsilon = DEFAULT_ZERO_TOLERANCE;
  189.         for (int key = 0; key < virtualSize; key++) {
  190.             double value = v.getEntry(key);
  191.             if (!isDefaultValue(value)) {
  192.                 entries.put(key, value);
  193.             }
  194.         }
  195.     }

  196.     /**
  197.      * Get the entries of this instance.
  198.      *
  199.      * @return the entries of this instance.
  200.      */
  201.     private OpenIntToDoubleHashMap getEntries() {
  202.         return entries;
  203.     }

  204.     /**
  205.      * Determine if this value is within epsilon of zero.
  206.      *
  207.      * @param value Value to test
  208.      * @return {@code true} if this value is within epsilon to zero,
  209.      * {@code false} otherwise.
  210.      */
  211.     protected boolean isDefaultValue(double value) {
  212.         return FastMath.abs(value) < epsilon;
  213.     }

  214.     /** {@inheritDoc} */
  215.     @Override
  216.     public RealVector add(RealVector v)
  217.         throws MathIllegalArgumentException {
  218.         checkVectorDimensions(v.getDimension());
  219.         if (v instanceof OpenMapRealVector) {
  220.             return add((OpenMapRealVector) v);
  221.         } else {
  222.             return super.add(v);
  223.         }
  224.     }

  225.     /**
  226.      * Optimized method to add two OpenMapRealVectors.
  227.      * It copies the larger vector, then iterates over the smaller.
  228.      *
  229.      * @param v Vector to add.
  230.      * @return the sum of {@code this} and {@code v}.
  231.      * @throws MathIllegalArgumentException if the dimensions do not match.
  232.      */
  233.     public OpenMapRealVector add(OpenMapRealVector v)
  234.         throws MathIllegalArgumentException {
  235.         checkVectorDimensions(v.getDimension());
  236.         boolean copyThis = entries.size() > v.entries.size();
  237.         OpenMapRealVector res = copyThis ? this.copy() : v.copy();
  238.         Iterator iter = copyThis ? v.entries.iterator() : entries.iterator();
  239.         OpenIntToDoubleHashMap randomAccess = copyThis ? entries : v.entries;
  240.         while (iter.hasNext()) {
  241.             iter.advance();
  242.             int key = iter.key();
  243.             if (randomAccess.containsKey(key)) {
  244.                 res.setEntry(key, randomAccess.get(key) + iter.value());
  245.             } else {
  246.                 res.setEntry(key, iter.value());
  247.             }
  248.         }
  249.         return res;
  250.     }

  251.     /**
  252.      * Optimized method to append a OpenMapRealVector.
  253.      * @param v vector to append
  254.      * @return The result of appending {@code v} to self
  255.      */
  256.     public OpenMapRealVector append(OpenMapRealVector v) {
  257.         OpenMapRealVector res = new OpenMapRealVector(this, v.getDimension());
  258.         Iterator iter = v.entries.iterator();
  259.         while (iter.hasNext()) {
  260.             iter.advance();
  261.             res.setEntry(iter.key() + virtualSize, iter.value());
  262.         }
  263.         return res;
  264.     }

  265.     /** {@inheritDoc} */
  266.     @Override
  267.     public OpenMapRealVector append(RealVector v) {
  268.         if (v instanceof OpenMapRealVector) {
  269.             return append((OpenMapRealVector) v);
  270.         } else {
  271.             final OpenMapRealVector res = new OpenMapRealVector(this, v.getDimension());
  272.             for (int i = 0; i < v.getDimension(); i++) {
  273.                 res.setEntry(i + virtualSize, v.getEntry(i));
  274.             }
  275.             return res;
  276.         }
  277.     }

  278.     /** {@inheritDoc} */
  279.     @Override
  280.     public OpenMapRealVector append(double d) {
  281.         OpenMapRealVector res = new OpenMapRealVector(this, 1);
  282.         res.setEntry(virtualSize, d);
  283.         return res;
  284.     }

  285.     /**
  286.      * {@inheritDoc}
  287.      */
  288.     @Override
  289.     public OpenMapRealVector copy() {
  290.         return new OpenMapRealVector(this);
  291.     }

  292.     /** {@inheritDoc} */
  293.     @Override
  294.     public OpenMapRealVector ebeDivide(RealVector v)
  295.         throws MathIllegalArgumentException {
  296.         checkVectorDimensions(v.getDimension());
  297.         OpenMapRealVector res = new OpenMapRealVector(this);
  298.         /*
  299.          * MATH-803: it is not sufficient to loop through non zero entries of
  300.          * this only. Indeed, if this[i] = 0d and v[i] = 0d, then
  301.          * this[i] / v[i] = NaN, and not 0d.
  302.          */
  303.         final int n = getDimension();
  304.         for (int i = 0; i < n; i++) {
  305.             res.setEntry(i, this.getEntry(i) / v.getEntry(i));
  306.         }
  307.         return res;
  308.     }

  309.     /** {@inheritDoc} */
  310.     @Override
  311.     public OpenMapRealVector ebeMultiply(RealVector v)
  312.         throws MathIllegalArgumentException {
  313.         checkVectorDimensions(v.getDimension());
  314.         OpenMapRealVector res = new OpenMapRealVector(this);
  315.         Iterator iter = entries.iterator();
  316.         while (iter.hasNext()) {
  317.             iter.advance();
  318.             res.setEntry(iter.key(), iter.value() * v.getEntry(iter.key()));
  319.         }
  320.         return res;
  321.     }

  322.     /** {@inheritDoc} */
  323.     @Override
  324.     public OpenMapRealVector getSubVector(int index, int n)
  325.         throws MathIllegalArgumentException {
  326.         checkIndex(index);
  327.         if (n < 0) {
  328.             throw new MathIllegalArgumentException(LocalizedCoreFormats.NUMBER_OF_ELEMENTS_SHOULD_BE_POSITIVE, n);
  329.         }
  330.         checkIndex(index + n - 1);
  331.         OpenMapRealVector res = new OpenMapRealVector(n);
  332.         int end = index + n;
  333.         Iterator iter = entries.iterator();
  334.         while (iter.hasNext()) {
  335.             iter.advance();
  336.             int key = iter.key();
  337.             if (key >= index && key < end) {
  338.                 res.setEntry(key - index, iter.value());
  339.             }
  340.         }
  341.         return res;
  342.     }

  343.     /** {@inheritDoc} */
  344.     @Override
  345.     public int getDimension() {
  346.         return virtualSize;
  347.     }

  348.     /**
  349.      * Optimized method to compute distance.
  350.      *
  351.      * @param v Vector to compute distance to.
  352.      * @return the distance from {@code this} and {@code v}.
  353.      * @throws MathIllegalArgumentException if the dimensions do not match.
  354.      */
  355.     public double getDistance(OpenMapRealVector v)
  356.         throws MathIllegalArgumentException {
  357.         checkVectorDimensions(v.getDimension());
  358.         Iterator iter = entries.iterator();
  359.         double res = 0;
  360.         while (iter.hasNext()) {
  361.             iter.advance();
  362.             int key = iter.key();
  363.             double delta;
  364.             delta = iter.value() - v.getEntry(key);
  365.             res += delta * delta;
  366.         }
  367.         iter = v.getEntries().iterator();
  368.         while (iter.hasNext()) {
  369.             iter.advance();
  370.             int key = iter.key();
  371.             if (!entries.containsKey(key)) {
  372.                 final double value = iter.value();
  373.                 res += value * value;
  374.             }
  375.         }
  376.         return FastMath.sqrt(res);
  377.     }

  378.     /** {@inheritDoc} */
  379.     @Override
  380.     public double getDistance(RealVector v) throws MathIllegalArgumentException {
  381.         checkVectorDimensions(v.getDimension());
  382.         if (v instanceof OpenMapRealVector) {
  383.             return getDistance((OpenMapRealVector) v);
  384.         } else {
  385.             return super.getDistance(v);
  386.         }
  387.     }

  388.     /** {@inheritDoc} */
  389.     @Override
  390.     public double getEntry(int index) throws MathIllegalArgumentException {
  391.         checkIndex(index);
  392.         return entries.get(index);
  393.     }

  394.     /**
  395.      * Distance between two vectors.
  396.      * This method computes the distance consistent with
  397.      * L<sub>1</sub> norm, i.e. the sum of the absolute values of
  398.      * elements differences.
  399.      *
  400.      * @param v Vector to which distance is requested.
  401.      * @return distance between this vector and {@code v}.
  402.      * @throws MathIllegalArgumentException if the dimensions do not match.
  403.      */
  404.     public double getL1Distance(OpenMapRealVector v)
  405.         throws MathIllegalArgumentException {
  406.         checkVectorDimensions(v.getDimension());
  407.         double max = 0;
  408.         Iterator iter = entries.iterator();
  409.         while (iter.hasNext()) {
  410.             iter.advance();
  411.             double delta = FastMath.abs(iter.value() - v.getEntry(iter.key()));
  412.             max += delta;
  413.         }
  414.         iter = v.getEntries().iterator();
  415.         while (iter.hasNext()) {
  416.             iter.advance();
  417.             int key = iter.key();
  418.             if (!entries.containsKey(key)) {
  419.                 double delta = FastMath.abs(iter.value());
  420.                 max +=  FastMath.abs(delta);
  421.             }
  422.         }
  423.         return max;
  424.     }

  425.     /** {@inheritDoc} */
  426.     @Override
  427.     public double getL1Distance(RealVector v)
  428.         throws MathIllegalArgumentException {
  429.         checkVectorDimensions(v.getDimension());
  430.         if (v instanceof OpenMapRealVector) {
  431.             return getL1Distance((OpenMapRealVector) v);
  432.         } else {
  433.             return super.getL1Distance(v);
  434.         }
  435.     }

  436.     /**
  437.      * Optimized method to compute LInfDistance.
  438.      *
  439.      * @param v Vector to compute distance from.
  440.      * @return the LInfDistance.
  441.      * @throws MathIllegalArgumentException if the dimensions do not match.
  442.      */
  443.     private double getLInfDistance(OpenMapRealVector v)
  444.         throws MathIllegalArgumentException {
  445.         checkVectorDimensions(v.getDimension());
  446.         double max = 0;
  447.         Iterator iter = entries.iterator();
  448.         while (iter.hasNext()) {
  449.             iter.advance();
  450.             double delta = FastMath.abs(iter.value() - v.getEntry(iter.key()));
  451.             if (delta > max) {
  452.                 max = delta;
  453.             }
  454.         }
  455.         iter = v.getEntries().iterator();
  456.         while (iter.hasNext()) {
  457.             iter.advance();
  458.             int key = iter.key();
  459.             if (!entries.containsKey(key) && iter.value() > max) {
  460.                 max = iter.value();
  461.             }
  462.         }
  463.         return max;
  464.     }

  465.     /** {@inheritDoc} */
  466.     @Override
  467.     public double getLInfDistance(RealVector v)
  468.         throws MathIllegalArgumentException {
  469.         checkVectorDimensions(v.getDimension());
  470.         if (v instanceof OpenMapRealVector) {
  471.             return getLInfDistance((OpenMapRealVector) v);
  472.         } else {
  473.             return super.getLInfDistance(v);
  474.         }
  475.     }

  476.     /** {@inheritDoc} */
  477.     @Override
  478.     public boolean isInfinite() {
  479.         boolean infiniteFound = false;
  480.         Iterator iter = entries.iterator();
  481.         while (iter.hasNext()) {
  482.             iter.advance();
  483.             final double value = iter.value();
  484.             if (Double.isNaN(value)) {
  485.                 return false;
  486.             }
  487.             if (Double.isInfinite(value)) {
  488.                 infiniteFound = true;
  489.             }
  490.         }
  491.         return infiniteFound;
  492.     }

  493.     /** {@inheritDoc} */
  494.     @Override
  495.     public boolean isNaN() {
  496.         Iterator iter = entries.iterator();
  497.         while (iter.hasNext()) {
  498.             iter.advance();
  499.             if (Double.isNaN(iter.value())) {
  500.                 return true;
  501.             }
  502.         }
  503.         return false;
  504.     }

  505.     /** {@inheritDoc} */
  506.     @Override
  507.     public OpenMapRealVector mapAdd(double d) {
  508.         return copy().mapAddToSelf(d);
  509.     }

  510.     /** {@inheritDoc} */
  511.     @Override
  512.     public OpenMapRealVector mapAddToSelf(double d) {
  513.         for (int i = 0; i < virtualSize; i++) {
  514.             setEntry(i, getEntry(i) + d);
  515.         }
  516.         return this;
  517.     }

  518.     /** {@inheritDoc} */
  519.     @Override
  520.     public void setEntry(int index, double value)
  521.         throws MathIllegalArgumentException {
  522.         checkIndex(index);
  523.         if (!isDefaultValue(value)) {
  524.             entries.put(index, value);
  525.         } else if (entries.containsKey(index)) {
  526.             entries.remove(index);
  527.         }
  528.     }

  529.     /** {@inheritDoc} */
  530.     @Override
  531.     public void setSubVector(int index, RealVector v)
  532.         throws MathIllegalArgumentException {
  533.         checkIndex(index);
  534.         checkIndex(index + v.getDimension() - 1);
  535.         for (int i = 0; i < v.getDimension(); i++) {
  536.             setEntry(i + index, v.getEntry(i));
  537.         }
  538.     }

  539.     /** {@inheritDoc} */
  540.     @Override
  541.     public void set(double value) {
  542.         for (int i = 0; i < virtualSize; i++) {
  543.             setEntry(i, value);
  544.         }
  545.     }

  546.     /**
  547.      * Optimized method to subtract OpenMapRealVectors.
  548.      *
  549.      * @param v Vector to subtract from {@code this}.
  550.      * @return the difference of {@code this} and {@code v}.
  551.      * @throws MathIllegalArgumentException if the dimensions do not match.
  552.      */
  553.     public OpenMapRealVector subtract(OpenMapRealVector v)
  554.         throws MathIllegalArgumentException {
  555.         checkVectorDimensions(v.getDimension());
  556.         OpenMapRealVector res = copy();
  557.         Iterator iter = v.getEntries().iterator();
  558.         while (iter.hasNext()) {
  559.             iter.advance();
  560.             int key = iter.key();
  561.             if (entries.containsKey(key)) {
  562.                 res.setEntry(key, entries.get(key) - iter.value());
  563.             } else {
  564.                 res.setEntry(key, -iter.value());
  565.             }
  566.         }
  567.         return res;
  568.     }

  569.     /** {@inheritDoc} */
  570.     @Override
  571.     public RealVector subtract(RealVector v)
  572.         throws MathIllegalArgumentException {
  573.         checkVectorDimensions(v.getDimension());
  574.         if (v instanceof OpenMapRealVector) {
  575.             return subtract((OpenMapRealVector) v);
  576.         } else {
  577.             return super.subtract(v);
  578.         }
  579.     }

  580.     /** {@inheritDoc} */
  581.     @Override
  582.     public OpenMapRealVector unitVector() throws MathRuntimeException {
  583.         OpenMapRealVector res = copy();
  584.         res.unitize();
  585.         return res;
  586.     }

  587.     /** {@inheritDoc} */
  588.     @Override
  589.     public void unitize() throws MathRuntimeException {
  590.         double norm = getNorm();
  591.         if (isDefaultValue(norm)) {
  592.             throw new MathRuntimeException(LocalizedCoreFormats.ZERO_NORM);
  593.         }
  594.         Iterator iter = entries.iterator();
  595.         while (iter.hasNext()) {
  596.             iter.advance();
  597.             entries.put(iter.key(), iter.value() / norm);
  598.         }
  599.     }

  600.     /** {@inheritDoc} */
  601.     @Override
  602.     public double[] toArray() {
  603.         double[] res = new double[virtualSize];
  604.         Iterator iter = entries.iterator();
  605.         while (iter.hasNext()) {
  606.             iter.advance();
  607.             res[iter.key()] = iter.value();
  608.         }
  609.         return res;
  610.     }

  611.     /**
  612.      * {@inheritDoc}
  613.      * Implementation Note: This works on exact values, and as a result
  614.      * it is possible for {@code a.subtract(b)} to be the zero vector, while
  615.      * {@code a.hashCode() != b.hashCode()}.
  616.      */
  617.     @Override
  618.     public int hashCode() {
  619.         final int prime = 31;
  620.         int result = 1;
  621.         long temp;
  622.         temp = Double.doubleToLongBits(epsilon);
  623.         result = prime * result + (int) (temp ^ (temp >>> 32));
  624.         result = prime * result + virtualSize;
  625.         Iterator iter = entries.iterator();
  626.         while (iter.hasNext()) {
  627.             iter.advance();
  628.             temp = Double.doubleToLongBits(iter.value());
  629.             result = prime * result + (int) (temp ^ (temp >>32));
  630.         }
  631.         return result;
  632.     }

  633.     /**
  634.      * {@inheritDoc}
  635.      * Implementation Note: This performs an exact comparison, and as a result
  636.      * it is possible for {@code a.subtract(b}} to be the zero vector, while
  637.      * {@code  a.equals(b) == false}.
  638.      */
  639.     @Override
  640.     public boolean equals(Object obj) {
  641.         if (this == obj) {
  642.             return true;
  643.         }
  644.         if (!(obj instanceof OpenMapRealVector)) {
  645.             return false;
  646.         }
  647.         OpenMapRealVector other = (OpenMapRealVector) obj;
  648.         if (virtualSize != other.virtualSize) {
  649.             return false;
  650.         }
  651.         if (Double.doubleToLongBits(epsilon) !=
  652.             Double.doubleToLongBits(other.epsilon)) {
  653.             return false;
  654.         }
  655.         Iterator iter = entries.iterator();
  656.         while (iter.hasNext()) {
  657.             iter.advance();
  658.             double test = other.getEntry(iter.key());
  659.             if (Double.doubleToLongBits(test) != Double.doubleToLongBits(iter.value())) {
  660.                 return false;
  661.             }
  662.         }
  663.         iter = other.getEntries().iterator();
  664.         while (iter.hasNext()) {
  665.             iter.advance();
  666.             double test = iter.value();
  667.             if (Double.doubleToLongBits(test) != Double.doubleToLongBits(getEntry(iter.key()))) {
  668.                 return false;
  669.             }
  670.         }
  671.         return true;
  672.     }

  673.     /** Get percentage of none zero elements as a decimal percent.
  674.      * @return the percentage of none zero elements as a decimal percent
  675.      */
  676.     public double getSparsity() {
  677.         return ((double) entries.size()) / getDimension();
  678.     }

  679.     /** {@inheritDoc} */
  680.     @Override
  681.     public java.util.Iterator<Entry> sparseIterator() {
  682.         return new OpenMapSparseIterator();
  683.     }

  684.     /**
  685.      * Implementation of {@code Entry} optimized for OpenMap.
  686.      * This implementation does not allow arbitrary calls to {@code setIndex}
  687.      * since the order in which entries are returned is undefined.
  688.      */
  689.     protected class OpenMapEntry extends Entry {
  690.         /** Iterator pointing to the entry. */
  691.         private final Iterator iter;

  692.         /**
  693.          * Build an entry from an iterator point to an element.
  694.          *
  695.          * @param iter Iterator pointing to the entry.
  696.          */
  697.         protected OpenMapEntry(Iterator iter) {
  698.             this.iter = iter;
  699.         }

  700.         /** {@inheritDoc} */
  701.         @Override
  702.         public double getValue() {
  703.             return iter.value();
  704.         }

  705.         /** {@inheritDoc} */
  706.         @Override
  707.         public void setValue(double value) {
  708.             entries.put(iter.key(), value);
  709.         }

  710.         /** {@inheritDoc} */
  711.         @Override
  712.         public int getIndex() {
  713.             return iter.key();
  714.         }

  715.     }

  716.     /**
  717.      * Iterator class to do iteration over just the non-zero elements.
  718.      * This implementation is fail-fast, so cannot be used to modify
  719.      * any zero element.
  720.      */
  721.     protected class OpenMapSparseIterator implements java.util.Iterator<Entry> {
  722.         /** Underlying iterator. */
  723.         private final Iterator iter;
  724.         /** Current entry. */
  725.         private final Entry current;

  726.         /** Simple constructor. */
  727.         protected OpenMapSparseIterator() {
  728.             iter = entries.iterator();
  729.             current = new OpenMapEntry(iter);
  730.         }

  731.         /** {@inheritDoc} */
  732.         @Override
  733.         public boolean hasNext() {
  734.             return iter.hasNext();
  735.         }

  736.         /** {@inheritDoc} */
  737.         @Override
  738.         public Entry next() {
  739.             iter.advance();
  740.             return current;
  741.         }

  742.         /** {@inheritDoc} */
  743.         @Override
  744.         public void remove() {
  745.             throw new UnsupportedOperationException("Not supported");
  746.         }
  747.     }
  748. }