diff --git a/src/Numerics/Constants.cs b/src/Numerics/Constants.cs
index 67504466..6352bbab 100644
--- a/src/Numerics/Constants.cs
+++ b/src/Numerics/Constants.cs
@@ -164,6 +164,11 @@ namespace MathNet.Numerics
///
public const int SizeOfDouble = sizeof(double);
+ ///
+ /// The size of a double in bytes.
+ ///
+ public const int SizeOfInt = sizeof(int);
+
///
/// The size of a float in bytes.
///
diff --git a/src/Numerics/LinearAlgebra/Double/SparseVector.cs b/src/Numerics/LinearAlgebra/Double/SparseVector.cs
new file mode 100644
index 00000000..fa2d00dc
--- /dev/null
+++ b/src/Numerics/LinearAlgebra/Double/SparseVector.cs
@@ -0,0 +1,1235 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+// http://mathnetnumerics.codeplex.com
+//
+// Copyright (c) 2009-2010 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+namespace MathNet.Numerics.LinearAlgebra.Double
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Globalization;
+ using NumberTheory;
+ using Properties;
+ using Threading;
+
+ public class SparseVector : Vector
+ {
+ ///
+ /// Gets the vector's internal data. The array containing the actual values; only the non-zero values are stored.
+ ///
+ private double[] NonZeroValues = new double[0];
+
+ ///
+ /// The indices of the non-zero entries.
+ ///
+ private int[] NonZeroIndices = new int[0];
+ ///
+ /// Returns the number of non zero elements in the vector.
+ ///
+ /// The number of non zero elements.
+ public int NonZerosCount
+ {
+ get;
+ private set;
+ }
+
+ #region Constructors
+ ///
+ /// Initializes a new instance of the class with a given size.
+ ///
+ ///
+ /// the size of the vector.
+ ///
+ ///
+ /// If is less than one.
+ ///
+ public SparseVector(int size) : base(size) { }
+
+ ///
+ /// Initializes a new instance of the class with a given size
+ /// and each element set to the given value;
+ ///
+ ///
+ /// the size of the vector.
+ ///
+ ///
+ /// the value to set each element to.
+ ///
+ ///
+ /// If is less than one.
+ ///
+ public SparseVector(int size, double value) : this(size)
+ {
+ if (value == 0.0) //Skip adding values
+ return;
+
+ // We already know that this vector is "full", let's allocate all needed memory
+ NonZeroValues = new double[size];
+ NonZeroIndices = new int[size];
+ NonZerosCount = size;
+
+ CommonParallel.For(
+ 0,
+ this.Count,
+ index =>
+ {
+ NonZeroValues[index] = value;
+ NonZeroIndices[index] = index;
+ });
+ }
+
+ ///
+ /// Initializes a new instance of the class by
+ /// copying the values from another.
+ ///
+ ///
+ /// The vector to create the new vector from.
+ ///
+ public SparseVector(Vector other) : this(other.Count)
+ {
+ var vector = other as SparseVector;
+ if (vector == null)
+ {
+ for (int i = 0; i < other.Count; i++ )
+ this[i] = other[i];
+ }
+ else
+ {
+ NonZeroValues = new double[vector.NonZerosCount];
+ NonZeroIndices = new int[vector.NonZerosCount];
+ NonZerosCount = vector.NonZerosCount;
+
+ // Lets copy only needed data. Portion of needed data is determined by NonZerosCount value
+ Buffer.BlockCopy(vector.NonZeroValues, 0, this.NonZeroValues, 0, vector.NonZerosCount * Constants.SizeOfDouble);
+ Buffer.BlockCopy(vector.NonZeroIndices, 0, this.NonZeroIndices, 0, vector.NonZerosCount * Constants.SizeOfInt);
+ }
+ }
+
+ ///
+ /// Initializes a new instance of the class by
+ /// copying the values from another.
+ ///
+ ///
+ /// The vector to create the new vector from.
+ ///
+ public SparseVector(SparseVector other) : this(other.Count)
+ {
+ // Lets copy only needed data. Portion of needed data is determined by NonZerosCount value
+ NonZeroValues = new double[other.NonZerosCount];
+ NonZeroIndices = new int[other.NonZerosCount];
+ NonZerosCount = other.NonZerosCount;
+
+ Buffer.BlockCopy(other.NonZeroValues, 0, this.NonZeroValues, 0, other.NonZerosCount * Constants.SizeOfDouble);
+ Buffer.BlockCopy(other.NonZeroIndices, 0, this.NonZeroIndices, 0, other.NonZerosCount * Constants.SizeOfInt);
+ }
+
+ ///
+ /// Initializes a new instance of the class for an array.
+ ///
+ /// The array to create this vector from.
+ /// The vector copy the array. Any changes to the vector will NOT change the array.
+ public SparseVector(double[] array) : this(array.Length)
+ {
+ for (int i = 0; i < array.Length; i++ )
+ this[i] = array[i];
+ }
+ #endregion
+
+ ///
+ /// Create a matrix based on this vector in column form (one single column).
+ ///
+ /// This vector as a column matrix.
+ public override Matrix ToColumnMatrix()
+ {
+ throw new NotImplementedException();
+ //var matrix = new SparseMatrix(this.Count, 1);
+ //CommonParallel.For(
+ // 0,
+ // this.Count,
+ // index => matrix[i, 0] = vector[index]);
+ //return matrix;
+ }
+
+ ///
+ /// Create a matrix based on this vector in row form (one single row).
+ ///
+ /// This vector as a row matrix.
+ public override Matrix ToRowMatrix()
+ {
+ throw new NotImplementedException();
+ //var matrix = new SparseMatrix(1, this.Count);
+ //CommonParallel.For(
+ // 0,
+ // this.Count,
+ // index => matrix[0, i] = vector[index]);
+ //return matrix;
+ }
+
+ private readonly object lockObject = new object();
+ /// Gets or sets the value at the given .
+ /// The index of the value to get or set.
+ /// The value of the vector at the given .
+ /// If is negative or
+ /// greater than the size of the vector.
+ public override double this[int index]
+ {
+ get
+ {
+ // If index is out of bounds
+ if ((index < 0) || (index >= Count))
+ {
+ throw new IndexOutOfRangeException();
+ }
+
+ lock (lockObject)
+ {
+ // Search if item idex exists in NonZeroIndices array in range "0 - real nonzero values count"
+ int itemIndex = Array.BinarySearch(NonZeroIndices, 0, NonZerosCount, index);
+ if (itemIndex >= 0)
+ return NonZeroValues[itemIndex];
+ }
+ return 0.0;
+ }
+ set
+ {
+ // If index is out of bounds
+ if ((index < 0) || (index >= Count))
+ {
+ throw new IndexOutOfRangeException();
+ }
+
+ lock (lockObject)
+ {
+ SetValue(index, value);
+ }
+ }
+ }
+
+ ///
+ /// Creates a matrix with the given dimensions using the same storage type
+ /// as this vector.
+ ///
+ ///
+ /// The number of rows.
+ ///
+ ///
+ /// The number of columns.
+ ///
+ ///
+ /// A matrix with the given dimensions.
+ ///
+ public override Matrix CreateMatrix(int rows, int columns)
+ {
+ throw new NotImplementedException();
+ //return new SparseMatrix(rows, columns);
+ }
+
+ ///
+ /// Creates a Vector of the given size using the same storage type
+ /// as this vector.
+ ///
+ ///
+ /// The size of the Vector to create.
+ ///
+ ///
+ /// The new Vector.
+ ///
+ public override Vector CreateVector(int size)
+ {
+ return new SparseVector(size);
+ }
+
+ ///
+ /// Copies the values of this vector into the target vector.
+ ///
+ ///
+ /// The vector to copy elements into.
+ ///
+ ///
+ /// If is .
+ ///
+ ///
+ /// If is not the same size as this vector.
+ ///
+ public override void CopyTo(Vector target)
+ {
+ if (target == null)
+ {
+ throw new ArgumentNullException("target");
+ }
+
+ if (this.Count != target.Count)
+ {
+ throw new ArgumentException(Resources.ArgumentVectorsSameLength, "target");
+ }
+
+ if (ReferenceEquals(this, target))
+ {
+ return;
+ }
+
+ var otherVector = target as SparseVector;
+ if (otherVector == null)
+ {
+ CommonParallel.For(
+ 0,
+ this.Count,
+ index => target[index] = this[index]);
+ }
+ else
+ {
+ // Lets copy only needed data. Portion of needed data is determined by NonZerosCount value
+ otherVector.NonZeroValues = new double[this.NonZerosCount];
+ otherVector.NonZeroIndices = new int[this.NonZerosCount];
+ otherVector.NonZerosCount = this.NonZerosCount;
+
+ Buffer.BlockCopy(this.NonZeroValues, 0, otherVector.NonZeroValues, 0, this.NonZerosCount * Constants.SizeOfDouble);
+ Buffer.BlockCopy(this.NonZeroIndices, 0, otherVector.NonZeroIndices, 0, this.NonZerosCount * Constants.SizeOfInt);
+ }
+ }
+
+ #region Operators and supplementary functions
+
+ // NOTE: There are no operators as:
+ // public static implicit operator SparseVector(double[] array)
+ // and
+ // public static implicit operator double[](SparseVector vector)
+ // as it is in DenseVector. Because when creating vector from double[] values are copied to internal storage and if user wants
+ // to get double[] he should call SparseVector.ToArray(), then double[] will be generated and returned to a user.\
+
+ // In DenseVector implementation reference to double[] is assigned to interanl storage when casting from double[] and returned
+ // when casting to double[]
+
+ ///
+ /// Adds a scalar to each element of the vector.
+ ///
+ /// The scalar to add.
+ public override void Add(double scalar)
+ {
+ if (scalar == 0.0)
+ {
+ return;
+ }
+ for (int i = 0; i < this.Count; i++ )
+ this[i] += scalar;
+ }
+
+ ///
+ /// Adds a scalar to each element of the vector and stores the result in the result vector.
+ ///
+ /// The scalar to add.
+ /// The vector to store the result of the addition.
+ /// If the result vector is .
+ /// If this vector and are not the same size.
+ public override void Add(double scalar, Vector result)
+ {
+ if (result == null)
+ {
+ throw new ArgumentNullException("result");
+ }
+
+ if (this.Count != result.Count)
+ {
+ throw new ArgumentException(Resources.ArgumentVectorsSameLength, "result");
+ }
+
+ this.CopyTo(result);
+ result.Add(scalar);
+ }
+
+ ///
+ /// Adds another vector to this vector.
+ ///
+ /// The vector to add to this one.
+ /// If the other vector is .
+ /// If this vector and are not the same size.
+ public override void Add(Vector other)
+ {
+ if (other == null)
+ {
+ throw new ArgumentNullException("other");
+ }
+
+ if (this.Count != other.Count)
+ {
+ throw new ArgumentException(Resources.ArgumentVectorsSameLength, "other");
+ }
+
+ var sparseVector = other as SparseVector;
+
+ if (sparseVector == null)
+ {
+ base.Add(other);
+ }
+ else
+ {
+ this.AddScaledSparceVector(1.0, sparseVector);
+ }
+ }
+
+ private void AddScaledSparceVector(double alpha, SparseVector other)
+ {
+ if (other == null)
+ {
+ throw new ArgumentNullException("other");
+ }
+
+ if (this.Count != other.Count)
+ {
+ throw new ArgumentException(Resources.ArgumentVectorsSameLength, "other");
+ }
+
+ if (alpha == 0.0)
+ {
+ return;
+ }
+
+ // I don't use ILinearAlgebraProvider because we will get no benefit due to "lock" in this[index]
+ // Possible fucniton in ILinearAlgebraProvider may be AddSparseVectorToScaledSparseVector(T[] y, int[] yIndices, T alpha, T[] x, int[] xIndices);
+ // But it require to develop value setting algorithm and due to "lock" it will be even more greedy then implemented below
+ if (ReferenceEquals(this, other))
+ {
+ // Adding the same instance of sparse vector. That means if we modify "this" then "other" will be modified too.
+ // To avoid such problem lets change values in internal storage of "this"
+ if (alpha == 1.0)
+ {
+ for (int i = 0; i < this.NonZerosCount; i++)
+ {
+ this.NonZeroValues[i] += this.NonZeroValues[i];
+ }
+ }
+ else if (alpha == -1.0)
+ {
+ NonZerosCount = 0; // Vector is subtracted from itself
+ }
+ else
+ {
+ for (int i = 0; i < this.NonZerosCount; i++)
+ {
+ this[other.NonZeroIndices[i]] += alpha * this.NonZeroValues[i];
+ }
+ }
+ }
+ else
+ {
+ // "this" and "other" are different objects, so by modifying "this" the "other" object will not be changed
+ if (alpha == 1.0)
+ {
+ for (int i = 0; i < other.NonZerosCount; i++)
+ {
+ this[other.NonZeroIndices[i]] += other.NonZeroValues[i];
+ }
+ }
+ else
+ {
+ for (int i = 0; i < other.NonZerosCount; i++)
+ {
+ this[other.NonZeroIndices[i]] += alpha * other.NonZeroValues[i];
+ }
+ }
+ }
+ }
+ ///
+ /// Adds another vector to this vector and stores the result into the result vector.
+ ///
+ /// The vector to add to this one.
+ /// The vector to store the result of the addition.
+ /// If the other vector is .
+ /// If the result vector is .
+ /// If this vector and are not the same size.
+ /// If this vector and are not the same size.
+ public override void Add(Vector other, Vector result)
+ {
+ if (result == null)
+ {
+ throw new ArgumentNullException("result");
+ }
+
+ if (this.Count != other.Count)
+ {
+ throw new ArgumentException(Resources.ArgumentVectorsSameLength, "other");
+ }
+
+ if (this.Count != result.Count)
+ {
+ throw new ArgumentException(Resources.ArgumentVectorsSameLength, "result");
+ }
+
+ if (ReferenceEquals(this, result) || ReferenceEquals(other, result))
+ {
+ var tmp = result.CreateVector(result.Count);
+ Add(other, tmp);
+ tmp.CopyTo(result);
+ }
+ else
+ {
+ this.CopyTo(result);
+ result.Add(other);
+ }
+ }
+
+ ///
+ /// Returns a Vector containing the same values of rightSide.
+ ///
+ /// This method is included for completeness.
+ /// The vector to get the values from.
+ /// A vector containing a the same values as .
+ /// If is .
+ public static Vector operator +(SparseVector rightSide)
+ {
+ if (rightSide == null)
+ {
+ throw new ArgumentNullException("rightSide");
+ }
+
+ return rightSide.Plus();
+ }
+
+ ///
+ /// Adds two Vectors together and returns the results.
+ ///
+ /// One of the vectors to add.
+ /// The other vector to add.
+ /// The result of the addition.
+ /// If and are not the same size.
+ /// If or is .
+ public static Vector operator +(SparseVector leftSide, SparseVector rightSide)
+ {
+ if (rightSide == null)
+ {
+ throw new ArgumentNullException("rightSide");
+ }
+
+ if (leftSide == null)
+ {
+ throw new ArgumentNullException("leftSide");
+ }
+
+ if (leftSide.Count != rightSide.Count)
+ {
+ throw new ArgumentException(Resources.ArgumentVectorsSameLength, "rightSide");
+ }
+
+ var ret = leftSide.Clone();
+ ret.Add(rightSide);
+ return ret;
+ }
+ ///
+ /// Subtracts a scalar from each element of the vector.
+ ///
+ /// The scalar to subtract.
+ public override void Subtract(double scalar)
+ {
+ if (scalar == 0.0)
+ {
+ return;
+ }
+ for (int i = 0; i < this.Count; i++)
+ this[i] -= scalar;
+ }
+
+ ///
+ /// Subtracts a scalar from each element of the vector and stores the result in the result vector.
+ ///
+ /// The scalar to subtract.
+ /// The vector to store the result of the subtraction.
+ /// If the result vector is .
+ /// If this vector and are not the same size.
+ public override void Subtract(double scalar, Vector result)
+ {
+ if (result == null)
+ {
+ throw new ArgumentNullException("result");
+ }
+
+ if (this.Count != result.Count)
+ {
+ throw new ArgumentException(Resources.ArgumentVectorsSameLength, "result");
+ }
+
+ this.CopyTo(result);
+ result.Subtract(scalar);
+ }
+
+ ///
+ /// Subtracts another vector from this vector.
+ ///
+ /// The vector to subtract from this one.
+ /// If the other vector is .
+ /// If this vector and are not the same size.
+ public override void Subtract(Vector other)
+ {
+ if (other == null)
+ {
+ throw new ArgumentNullException("other");
+ }
+
+ if (this.Count != other.Count)
+ {
+ throw new ArgumentException(Resources.ArgumentVectorsSameLength, "other");
+ }
+
+ var sparseVector = other as SparseVector;
+
+ if (sparseVector == null)
+ {
+ base.Subtract(other);
+ }
+ else
+ {
+ this.AddScaledSparceVector(-1.0, sparseVector);
+ }
+ }
+
+ ///
+ /// Subtracts another vector to this vector and stores the result into the result vector.
+ ///
+ /// The vector to subtract from this one.
+ /// The vector to store the result of the subtraction.
+ /// If the other vector is .
+ /// If the result vector is .
+ /// If this vector and are not the same size.
+ /// If this vector and are not the same size.
+ public override void Subtract(Vector other, Vector result)
+ {
+ if (result == null)
+ {
+ throw new ArgumentNullException("result");
+ }
+
+ if (this.Count != other.Count)
+ {
+ throw new ArgumentException(Resources.ArgumentVectorsSameLength, "other");
+ }
+
+ if (this.Count != result.Count)
+ {
+ throw new ArgumentException(Resources.ArgumentVectorsSameLength, "result");
+ }
+
+ if (ReferenceEquals(this, result) || ReferenceEquals(other, result))
+ {
+ var tmp = result.CreateVector(result.Count);
+ Subtract(other, tmp);
+ tmp.CopyTo(result);
+ }
+ else
+ {
+ this.CopyTo(result);
+ result.Subtract(other);
+ }
+ }
+
+ ///
+ /// Returns a Vector containing the negated values of rightSide.
+ ///
+ /// The vector to get the values from.
+ /// A vector containing the negated values as .
+ /// If is .
+ public static Vector operator -(SparseVector rightSide)
+ {
+ if (rightSide == null)
+ {
+ throw new ArgumentNullException("rightSide");
+ }
+
+ return rightSide.Negate();
+ }
+
+ ///
+ /// Subtracts two Vectors and returns the results.
+ ///
+ /// The vector to subtract from.
+ /// The vector to subtract.
+ /// The result of the subtraction.
+ /// If and are not the same size.
+ /// If or is .
+ public static Vector operator -(SparseVector leftSide, SparseVector rightSide)
+ {
+ if (rightSide == null)
+ {
+ throw new ArgumentNullException("rightSide");
+ }
+
+ if (leftSide == null)
+ {
+ throw new ArgumentNullException("leftSide");
+ }
+
+ if (leftSide.Count != rightSide.Count)
+ {
+ throw new ArgumentException(Resources.ArgumentVectorsSameLength, "rightSide");
+ }
+
+ var ret = leftSide.Clone();
+ ret.Subtract(rightSide);
+ return ret;
+ }
+
+ ///
+ /// Returns a negated vector.
+ ///
+ /// The negated vector.
+ /// Added as an alternative to the unary negation operator.
+ public override Vector Negate()
+ {
+ var result = new SparseVector(this.Count)
+ {
+ NonZeroValues = new double[this.NonZerosCount],
+ NonZeroIndices = new int[this.NonZerosCount],
+ NonZerosCount = this.NonZerosCount
+ };
+
+ Buffer.BlockCopy(this.NonZeroIndices, 0, result.NonZeroIndices, 0, this.NonZerosCount * Constants.SizeOfInt);
+
+ CommonParallel.For(
+ 0,
+ this.NonZerosCount,
+ index => result.NonZeroValues[index] = -this.NonZeroValues[index]);
+
+ return result;
+ }
+
+ ///
+ /// Multiplies a scalar to each element of the vector.
+ ///
+ /// The scalar to multiply.
+ public override void Multiply(double scalar)
+ {
+ if (scalar == 1.0)
+ {
+ return;
+ }
+ if (scalar == 0)
+ {
+ NonZerosCount = 0; // Set array empty
+ }
+ Control.LinearAlgebraProvider.ScaleArray(scalar, this.NonZeroValues);
+ }
+
+ ///
+ /// Computes the dot product between this vector and another vector.
+ ///
+ /// The other vector to add.
+ /// The result of the addition.
+ /// If is not of the same size.
+ /// If is .
+ public override double DotProduct(Vector other)
+ {
+ if (other == null)
+ {
+ throw new ArgumentNullException("other");
+ }
+
+ if (this.Count != other.Count)
+ {
+ throw new ArgumentException(Resources.ArgumentVectorsSameLength, "other");
+ }
+
+ double result = 0;
+
+ // base implementation iterates though all elements, but we need only take non-zeros
+ for (var i = 0; i < this.NonZerosCount; i++)
+ {
+ result += this.NonZeroValues[i] * other[this.NonZeroIndices[i]];
+ }
+ return result;
+ }
+
+ ///
+ /// Multiplies a vector with a scalar.
+ ///
+ /// The vector to scale.
+ /// The scalar value.
+ /// The result of the multiplication.
+ /// If is .
+ public static SparseVector operator *(SparseVector leftSide, double rightSide)
+ {
+ if (leftSide == null)
+ {
+ throw new ArgumentNullException("leftSide");
+ }
+
+ var ret = (SparseVector)leftSide.Clone();
+ ret.Multiply(rightSide);
+ return ret;
+ }
+
+ ///
+ /// Multiplies a vector with a scalar.
+ ///
+ /// The scalar value.
+ /// The vector to scale.
+ /// The result of the multiplication.
+ /// If is .
+ public static SparseVector operator *(double leftSide, SparseVector rightSide)
+ {
+ if (rightSide == null)
+ {
+ throw new ArgumentNullException("rightSide");
+ }
+
+ var ret = (SparseVector)rightSide.Clone();
+ ret.Multiply(leftSide);
+ return ret;
+ }
+
+ ///
+ /// Computes the dot product between two Vectors.
+ ///
+ /// The left row vector.
+ /// The right column vector.
+ /// The dot product between the two vectors.
+ /// If and are not the same size.
+ /// If or is .
+ public static double operator *(SparseVector leftSide, SparseVector rightSide)
+ {
+ if (rightSide == null)
+ {
+ throw new ArgumentNullException("rightSide");
+ }
+
+ if (leftSide == null)
+ {
+ throw new ArgumentNullException("leftSide");
+ }
+
+ if (leftSide.Count != rightSide.Count)
+ {
+ throw new ArgumentException(Resources.ArgumentVectorsSameLength, "rightSide");
+ }
+
+ return leftSide.DotProduct(rightSide);
+ }
+
+ ///
+ /// Divides a vector with a scalar.
+ ///
+ /// The vector to divide.
+ /// The scalar value.
+ /// The result of the division.
+ /// If is .
+ public static SparseVector operator /(SparseVector leftSide, double rightSide)
+ {
+ if (leftSide == null)
+ {
+ throw new ArgumentNullException("leftSide");
+ }
+ var ret = (SparseVector)leftSide.Clone();
+ ret.Multiply(1.0 / rightSide);
+ return ret;
+ }
+ #endregion
+
+ #region Vector Norms
+
+ ///
+ /// Euclidean Norm also known as 2-Norm.
+ ///
+ /// Scalar ret = sqrt(sum(this[i]^2))
+ public override double Norm()
+ {
+ var sum = 0.0;
+
+ for (var i = 0; i < this.Count; i++)
+ {
+ sum = SpecialFunctions.Hypotenuse(sum, this[i]);
+ }
+
+ return sum;
+ }
+
+ ///
+ /// 1-Norm also known as Manhattan Norm or Taxicab Norm.
+ ///
+ /// Scalar ret = sum(abs(this[i]))
+ public override double Norm1()
+ {
+ return CommonParallel.Aggregate(
+ 0,
+ this.NonZerosCount,
+ index => Math.Abs(this.NonZeroValues[index]));
+ }
+
+ ///
+ /// Computes the p-Norm.
+ ///
+ /// The p value.
+ /// Scalar ret = (sum(abs(this[i])^p))^(1/p)
+ public override double NormP(int p)
+ {
+ if (1 > p)
+ {
+ throw new ArgumentOutOfRangeException("p");
+ }
+
+ if (1 == p)
+ {
+ return this.Norm1();
+ }
+
+ if (2 == p)
+ {
+ return this.Norm();
+ }
+
+ var sum = CommonParallel.Aggregate(
+ 0,
+ this.NonZerosCount,
+ index => Math.Pow(Math.Abs(this.NonZeroValues[index]), p));
+
+ return Math.Pow(sum, 1.0 / p);
+ }
+
+ ///
+ /// Infinity Norm.
+ ///
+ /// Scalar ret = max(abs(this[i]))
+ public override double NormInfinity()
+ {
+ return CommonParallel.Select(
+ 0,
+ this.NonZerosCount,
+ (index, localData) => localData = Math.Max(localData, Math.Abs(this.NonZeroValues[index])), Math.Max);
+ }
+ #endregion
+
+ #region Parse Functions
+
+ ///
+ /// Creates a double sparse vector based on a string. The string can be in the following formats (without the
+ /// quotes): 'n', 'n,n,..', '(n,n,..)', '[n,n,...]', where n is a double.
+ ///
+ ///
+ /// A double sparce vector containing the values specified by the given string.
+ ///
+ ///
+ /// The string to parse.
+ ///
+ public static SparseVector Parse(string value)
+ {
+ return Parse(value, null);
+ }
+
+ ///
+ /// Creates a double sparse vector based on a string. The string can be in the following formats (without the
+ /// quotes): 'n', 'n,n,..', '(n,n,..)', '[n,n,...]', where n is a double.
+ ///
+ ///
+ /// A double sparce vector containing the values specified by the given string.
+ ///
+ ///
+ /// the string to parse.
+ ///
+ ///
+ /// An that supplies culture-specific formatting information.
+ ///
+ public static SparseVector Parse(string value, IFormatProvider formatProvider)
+ {
+ if (value == null)
+ {
+ throw new ArgumentNullException(value);
+ }
+
+ value = value.Trim();
+ if (value.Length == 0)
+ {
+ throw new FormatException();
+ }
+
+ // strip out parens
+ if (value.StartsWith("(", StringComparison.Ordinal))
+ {
+ if (!value.EndsWith(")", StringComparison.Ordinal))
+ {
+ throw new FormatException();
+ }
+
+ value = value.Substring(1, value.Length - 2).Trim();
+ }
+
+ if (value.StartsWith("[", StringComparison.Ordinal))
+ {
+ if (!value.EndsWith("]", StringComparison.Ordinal))
+ {
+ throw new FormatException();
+ }
+
+ value = value.Substring(1, value.Length - 2).Trim();
+ }
+
+ // keywords
+ var textInfo = formatProvider.GetTextInfo();
+ var keywords = new[] { textInfo.ListSeparator };
+
+ // lexing
+ var tokens = new LinkedList();
+ GlobalizationHelper.Tokenize(tokens.AddFirst(value), keywords, 0);
+ var token = tokens.First;
+
+ if (token == null || tokens.Count.IsEven())
+ {
+ throw new FormatException();
+ }
+
+ // parsing
+ var data = new double[(tokens.Count + 1) >> 1];
+ for (var i = 0; i < data.Length; i++)
+ {
+ if (token == null || token.Value == textInfo.ListSeparator)
+ {
+ throw new FormatException();
+ }
+
+ data[i] = Double.Parse(token.Value, NumberStyles.Any, formatProvider);
+
+ token = token.Next;
+ if (token != null)
+ {
+ token = token.Next;
+ }
+ }
+
+ return new SparseVector(data);
+ }
+
+ ///
+ /// Converts the string representation of a real sparse vector to double-precision sparse vector equivalent.
+ /// A return value indicates whether the conversion succeeded or failed.
+ ///
+ ///
+ /// A string containing a real vector to convert.
+ ///
+ ///
+ /// The parsed value.
+ ///
+ ///
+ /// If the conversion succeeds, the result will contain a complex number equivalent to value.
+ /// Otherwise the result will be null.
+ ///
+ public static bool TryParse(string value, out SparseVector result)
+ {
+ return TryParse(value, null, out result);
+ }
+
+ ///
+ /// Converts the string representation of a real sparse vector to double-precision sparse vector equivalent.
+ /// A return value indicates whether the conversion succeeded or failed.
+ ///
+ ///
+ /// A string containing a real vector to convert.
+ ///
+ ///
+ /// An that supplies culture-specific formatting information about value.
+ ///
+ ///
+ /// The parsed value.
+ ///
+ ///
+ /// If the conversion succeeds, the result will contain a complex number equivalent to value.
+ /// Otherwise the result will be null.
+ ///
+ public static bool TryParse(string value, IFormatProvider formatProvider, out SparseVector result)
+ {
+ bool ret;
+ try
+ {
+ result = Parse(value, formatProvider);
+ ret = true;
+ }
+ catch (ArgumentNullException)
+ {
+ result = null;
+ ret = false;
+ }
+ catch (FormatException)
+ {
+ result = null;
+ ret = false;
+ }
+
+ return ret;
+ }
+
+ #endregion
+
+ #region Implementation
+ ///
+ /// Delete, Add or Update the value in NonZeroValues and NonZeroIndices
+ ///
+ /// Value real index in array
+ /// Value
+ /// This method assume that index is between 0 and Array Size
+ private void SetValue(int index, double value)
+ {
+ // Search if "index" already exists in range "0 - real nonzero values count"
+ int itemIndex = Array.BinarySearch(NonZeroIndices, 0, NonZerosCount, index);
+
+ if (itemIndex >= 0)
+ {
+ // Item already exist at itemIndex
+ if (value == 0.0)
+ {
+ // Value is zero. Let's delete it from Values and Indices array
+ for (int i = itemIndex + 1; i < NonZerosCount; i++)
+ {
+ NonZeroValues[i - 1] = NonZeroValues[i];
+ NonZeroIndices[i - 1] = NonZeroIndices[i];
+ }
+ NonZerosCount -= 1;
+
+ // Check if the storage needs to be shrink. This is reasonable to do if
+ // there are a lot of non-zero elements and storage is two times bigger
+ if ((NonZerosCount > 1024) && (NonZerosCount < NonZeroIndices.Length / 2))
+ {
+ Array.Resize(ref NonZeroValues, NonZerosCount);
+ Array.Resize(ref NonZeroIndices, NonZerosCount);
+ }
+
+ }
+ else
+ {
+ NonZeroValues[itemIndex] = value;
+ }
+ }
+ else
+ {
+ itemIndex = ~itemIndex; //Index where to put new value
+
+ // Check if the storage needs to be increased
+ if (NonZerosCount == NonZeroValues.Length)
+ {
+ // Value and Indices arrays are completely full so we increase the size
+ int size = Math.Min(NonZeroValues.Length + GrowthSize(), Count);
+ Array.Resize(ref NonZeroValues, size);
+ Array.Resize(ref NonZeroIndices, size);
+ }
+
+ // Move all values (with an position larger than index) in the value array
+ // to the next position
+ // move all values (with an position larger than index) in the columIndices
+ // array to the next position
+ for (int i = NonZerosCount - 1; i > itemIndex - 1; i--)
+ {
+ NonZeroValues[i + 1] = NonZeroValues[i];
+ NonZeroIndices[i + 1] = NonZeroIndices[i];
+ }
+
+ // Add the value and the column index
+ NonZeroValues[itemIndex] = value;
+ NonZeroIndices[itemIndex] = index;
+
+ // increase the number of non-zero numbers by one
+ NonZerosCount += 1;
+ }
+ }
+ ///
+ /// Calculates the amount with which to grow the storage array's if they need to be
+ /// increased in size.
+ ///
+ private int GrowthSize()
+ {
+ int delta;
+ if (NonZeroValues.Length > 1024)
+ {
+ delta = NonZeroValues.Length / 4;
+ }
+ else
+ {
+ if (NonZeroValues.Length > 256)
+ {
+ delta = 512;
+ }
+ else
+ {
+ delta = NonZeroValues.Length > 64 ? 128 : 32;
+ }
+ }
+ return delta;
+ }
+
+ #endregion
+
+ #region System.Object override
+ ///
+ /// Check equality. If this is regular vector, then chek by base implementation. If Sparse - use own equition
+ ///
+ /// Object to compare
+ ///
+ public override bool Equals(object obj)
+ {
+ var sparseVector = obj as SparseVector;
+
+ if (sparseVector == null)
+ return base.Equals(obj);
+
+ // Accept if the argument is the same object as this.
+ if (ReferenceEquals(this, sparseVector))
+ {
+ return true;
+ }
+
+ if ((this.Count != sparseVector.Count) || (this.NonZerosCount != sparseVector.NonZerosCount))
+ {
+ return false;
+ }
+
+ // If all else fails, perform element wise comparison.
+ for (var index = 0; index < this.NonZerosCount; index++)
+ {
+ if (!this.NonZeroValues[index].AlmostEqual(sparseVector.NonZeroValues[index]) || (this.NonZeroIndices[index] != sparseVector.NonZeroIndices[index]))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+ ///
+ /// Returns a hash code for this instance.
+ ///
+ ///
+ /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
+ ///
+ public override int GetHashCode()
+ {
+ var hashNum = Math.Min(this.NonZerosCount, 20);
+ long hash = 0;
+ for (var i = 0; i < hashNum; i++)
+ {
+#if SILVERLIGHT
+ hash ^= Precision.DoubleToInt64Bits(this.NonZeroValues[i]);
+#else
+ hash ^= BitConverter.DoubleToInt64Bits(this.NonZeroValues[i]);
+#endif
+ }
+
+ return BitConverter.ToInt32(BitConverter.GetBytes(hash), 4);
+ }
+#endregion
+ }
+}
diff --git a/src/Numerics/Numerics.csproj b/src/Numerics/Numerics.csproj
index 2c45494a..ccac2022 100644
--- a/src/Numerics/Numerics.csproj
+++ b/src/Numerics/Numerics.csproj
@@ -151,6 +151,7 @@
+
diff --git a/src/Numerics/Version.cs b/src/Numerics/Version.cs
index 88bdb892..018e917a 100644
--- a/src/Numerics/Version.cs
+++ b/src/Numerics/Version.cs
@@ -22,9 +22,11 @@
// OTHER DEALINGS IN THE SOFTWARE.
//
/* This file is automatically generated - do not modify it. Change Version.tt instead.
- Last generated on: 15/05/2010 20:53:09
+ Last generated on: 04.06.2010 14:16:56
*/
+
using System.Reflection;
-[assembly: AssemblyVersion("2010.05.15.1253")]
-[assembly: AssemblyFileVersion("2010.05.15.1253")]
\ No newline at end of file
+
+[assembly: AssemblyVersion("2010.06.4.856")]
+[assembly: AssemblyFileVersion("2010.06.4.856")]
\ No newline at end of file
diff --git a/src/UnitTests/LinearAlgebraTests/Double/SparseVectorTest.TextHandling.cs b/src/UnitTests/LinearAlgebraTests/Double/SparseVectorTest.TextHandling.cs
new file mode 100644
index 00000000..09536b83
--- /dev/null
+++ b/src/UnitTests/LinearAlgebraTests/Double/SparseVectorTest.TextHandling.cs
@@ -0,0 +1,132 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+// http://mathnetnumerics.codeplex.com
+//
+// Copyright (c) 2009-2010 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
+{
+ using System;
+ using System.Globalization;
+ using LinearAlgebra.Double;
+ using MbUnit.Framework;
+
+ public class SparseVectorTextHandlingTest
+ {
+ [Test]
+ [Row("2", "2")]
+ [Row("(3)", "3")]
+ [Row("[1,2,3]", "1,2,3")]
+ [Row(" [ 1 , 2 , 3 ] ", "1,2,3")]
+ [Row(" [ -1 , 2 , +3 ] ", "-1,2,3")]
+ [Row(" [1.2,3.4 , 5.6] ", "1.2,3.4,5.6")]
+ public void CanParseDoubleSparseVectorsWithInvariant(string stringToParse, string expectedToString)
+ {
+ var formatProvider = CultureInfo.InvariantCulture;
+ SparseVector vector = SparseVector.Parse(stringToParse, formatProvider);
+
+ Assert.AreEqual(expectedToString, vector.ToString(formatProvider));
+ }
+
+ [Test]
+ [Row(" 1.2,3.4 , 5.6 ", "1.2,3.4,5.6", "en-US")]
+ [Row(" 1.2;3.4 ; 5.6 ", "1.2;3.4;5.6", "de-CH")]
+ [Row(" 1,2;3,4 ; 5,6 ", "1,2;3,4;5,6", "de-DE")]
+ public void CanParseDoubleSparseVectorsWithCulture(string stringToParse, string expectedToString, string culture)
+ {
+ var formatProvider = CultureInfo.GetCultureInfo(culture);
+ SparseVector vector = SparseVector.Parse(stringToParse, formatProvider);
+
+ Assert.AreEqual(expectedToString, vector.ToString(formatProvider));
+ }
+
+ [Test]
+ [Row("15")]
+ [Row("1{0}2{1}3{0}4{1}5{0}6")]
+ public void CanParseDoubleSparseVectors(string vectorAsString)
+ {
+ var mappedString = String.Format(
+ vectorAsString,
+ CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator,
+ CultureInfo.CurrentCulture.TextInfo.ListSeparator);
+
+ SparseVector vector = SparseVector.Parse(mappedString);
+
+ Assert.AreEqual(mappedString, vector.ToString());
+ }
+
+ [Test]
+ [MultipleAsserts]
+ public void ParseThrowsFormatExceptionIfMissingClosingParen()
+ {
+ Assert.Throws(() => SparseVector.Parse("(1"));
+ Assert.Throws(() => SparseVector.Parse("[1"));
+ }
+
+ [Test]
+ public void CanTryParseDoubleSparseVector()
+ {
+ var data = new[] { 1.2, 3.4, 5.6e-78 };
+ var text = String.Format(
+ "{1}{0}{2}{0}{3}",
+ CultureInfo.CurrentCulture.TextInfo.ListSeparator,
+ data[0],
+ data[1],
+ data[2]);
+
+ SparseVector vector;
+ var ret = SparseVector.TryParse(text, out vector);
+ Assert.IsTrue(ret);
+ AssertHelpers.AlmostEqualList(data, vector.ToArray(), 1e-15);
+
+ ret = SparseVector.TryParse(text, CultureInfo.CurrentCulture, out vector);
+ Assert.IsTrue(ret);
+ AssertHelpers.AlmostEqualList(data, vector.ToArray(), 1e-15);
+ }
+
+ [Test]
+ [Row(null)]
+ [Row("")]
+ [Row(",")]
+ [Row("1,")]
+ [Row(",1")]
+ [Row("1,2,")]
+ [Row(",1,2,")]
+ [Row("1,,2,,3")]
+ [Row("1e+")]
+ [Row("1e")]
+ [Row("()")]
+ [Row("[ ]")]
+ public void TryParseReturnsFalseWhenGivenBadValueWithInvariant(string str)
+ {
+ SparseVector vector;
+ var ret = SparseVector.TryParse(str, CultureInfo.InvariantCulture, out vector);
+ Assert.IsFalse(ret);
+ Assert.IsNull(vector);
+ }
+ }
+}
diff --git a/src/UnitTests/LinearAlgebraTests/Double/SparseVectorTest.cs b/src/UnitTests/LinearAlgebraTests/Double/SparseVectorTest.cs
new file mode 100644
index 00000000..75a96424
--- /dev/null
+++ b/src/UnitTests/LinearAlgebraTests/Double/SparseVectorTest.cs
@@ -0,0 +1,369 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+// http://mathnetnumerics.codeplex.com
+//
+// Copyright (c) 2009-2010 Math.NET
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double
+{
+ using System;
+ using System.Collections.Generic;
+ using MbUnit.Framework;
+ using LinearAlgebra.Double;
+
+ public class SparseVectorTest : VectorTests
+ {
+ protected override Vector CreateVector(int size)
+ {
+ return new SparseVector(size);
+ }
+
+ protected override Vector CreateVector(IList data)
+ {
+ var vector = new SparseVector(data.Count);
+ for (var index = 0; index < data.Count; index++)
+ {
+ vector[index] = data[index];
+ }
+ return vector;
+ }
+
+ #region Test similar to DenseVector
+ [Test]
+ [MultipleAsserts]
+ public void CanCreateSparseVectorFromArray()
+ {
+ var data = new double[_data.Length];
+ System.Array.Copy(_data, data, _data.Length);
+ var vector = new SparseVector(data);
+
+ //Assert.AreSame(data, vector.ToArray()); There is no way to cast SparseVector to double[], so "vector.ToArray()" and "array" have different references
+ for (var i = 0; i < data.Length; i++)
+ {
+ Assert.AreEqual(data[i], vector[i]);
+ }
+
+ // vector and data are different instances actually
+ //vector[0] = 100.0;
+ //Assert.AreEqual(100.0, data[0]);
+ }
+
+ [Test]
+ [MultipleAsserts]
+ public void CanCreateSparseVectorFromAnotherSparseVector()
+ {
+ var vector = new SparseVector(_data);
+ var other = new SparseVector(vector);
+
+
+ Assert.AreNotSame(vector, other);
+ for (var i = 0; i < _data.Length; i++)
+ {
+ Assert.AreEqual(vector[i], other[i]);
+ }
+ }
+
+ [Test]
+ [MultipleAsserts]
+ public void CanCreateSparseVectorFromAnotherVector()
+ {
+ var vector = (Vector)new SparseVector(_data);
+ var other = new SparseVector(vector);
+
+ Assert.AreNotSame(vector, other);
+ for (var i = 0; i < _data.Length; i++)
+ {
+ Assert.AreEqual(vector[i], other[i]);
+ }
+ }
+
+ [Test]
+ [MultipleAsserts]
+ public void CanCreateSparseVectorFromUserDefinedVector()
+ {
+ var vector = new UserDefinedVector(_data);
+ var other = new SparseVector(vector);
+
+ for (var i = 0; i < _data.Length; i++)
+ {
+ Assert.AreEqual(vector[i], other[i]);
+ }
+ }
+
+ [Test]
+ public void CanCreateSparseVectorWithConstantValues()
+ {
+ var vector = new SparseVector(5, 5);
+ Assert.ForAll(vector, value => value == 5);
+ }
+
+ // TODO: Remove [Ignore] when SparseMatrix developed
+ [Test]
+ [MultipleAsserts]
+ [Ignore]
+ public void CanCreateSparseMatrix()
+ {
+ var vector = new SparseVector(3);
+ var matrix = vector.CreateMatrix(2, 3);
+ Assert.AreEqual(2, matrix.RowCount);
+ Assert.AreEqual(3, matrix.ColumnCount);
+ }
+
+
+ [Test]
+ [MultipleAsserts]
+ public void CanConvertSparseVectorToArray()
+ {
+ var vector = new SparseVector(_data);
+ var array = vector.ToArray();
+ Assert.IsInstanceOfType(typeof(double[]), array);
+ //Assert.AreSame(vector.ToArray(), array); There is no way to cast SparseVector to double[], so "vector.ToArray()" and "array" have different references
+ Assert.AreElementsEqual(vector, array);
+ }
+
+ [Test]
+ [MultipleAsserts]
+ public void CanConvertArrayToSparseVector()
+ {
+ var array = new[] { 0.0, 1.0, 2.0, 3.0, 4.0 };
+ var vector = new SparseVector(array);
+ Assert.IsInstanceOfType(typeof(SparseVector), vector);
+ Assert.AreElementsEqual(array, array);
+ }
+
+ [Test]
+ public void CanCallUnaryPlusOperatorOnSparseVector()
+ {
+ var vector = new SparseVector(_data);
+ var other = +vector;
+ Assert.AreSame(vector, other, "Should be the same vector");
+ }
+
+ [Test]
+ [MultipleAsserts]
+ public void CanAddTwoSparseVectorsUsingOperator()
+ {
+ var vector = new SparseVector(_data);
+ var other = new SparseVector(_data);
+ var result = vector + other;
+
+ for (var i = 0; i < _data.Length; i++)
+ {
+ Assert.AreEqual(_data[i], vector[i], "Making sure the original vector wasn't modified.");
+ Assert.AreEqual(_data[i], other[i], "Making sure the original vector wasn't modified.");
+ Assert.AreEqual(_data[i] * 2.0, result[i]);
+ }
+ }
+
+ [Test]
+ public void CanCallUnaryNegationOperatorOnDenseVector()
+ {
+ var vector = new SparseVector(_data);
+ var other = -vector;
+ for (var i = 0; i < _data.Length; i++)
+ {
+ Assert.AreEqual(-_data[i], other[i]);
+ }
+ }
+
+ [Test]
+ [MultipleAsserts]
+ public void CanSubtractTwoSparseVectorsUsingOperator()
+ {
+ var vector = new SparseVector(_data);
+ var other = new SparseVector(_data);
+ var result = vector - other;
+
+ for (var i = 0; i < _data.Length; i++)
+ {
+ Assert.AreEqual(_data[i], vector[i], "Making sure the original vector wasn't modified.");
+ Assert.AreEqual(_data[i], other[i], "Making sure the original vector wasn't modified.");
+ Assert.AreEqual(0.0, result[i]);
+ }
+ }
+
+ [Test]
+ [MultipleAsserts]
+ public void CanMultiplySparseVectorByScalarUsingOperators()
+ {
+ var vector = new SparseVector(_data);
+ vector = vector * 2.0;
+
+ for (var i = 0; i < _data.Length; i++)
+ {
+ Assert.AreEqual(_data[i] * 2.0, vector[i]);
+ }
+
+ vector = vector * 1.0;
+ for (var i = 0; i < _data.Length; i++)
+ {
+ Assert.AreEqual(_data[i] * 2.0, vector[i]);
+ }
+
+ vector = new SparseVector(_data);
+ vector = 2.0 * vector;
+
+ for (var i = 0; i < _data.Length; i++)
+ {
+ Assert.AreEqual(_data[i] * 2.0, vector[i]);
+ }
+
+ vector = 1.0 * vector;
+ for (var i = 0; i < _data.Length; i++)
+ {
+ Assert.AreEqual(_data[i] * 2.0, vector[i]);
+ }
+ }
+
+ [Test]
+ [MultipleAsserts]
+ public void CanDivideSparseVectorByScalarUsingOperators()
+ {
+ var vector = new SparseVector(_data);
+ vector = vector / 2.0;
+
+ for (var i = 0; i < _data.Length; i++)
+ {
+ Assert.AreEqual(_data[i] / 2.0, vector[i]);
+ }
+
+ vector = vector / 1.0;
+ for (var i = 0; i < _data.Length; i++)
+ {
+ Assert.AreEqual(_data[i] / 2.0, vector[i]);
+ }
+ }
+ #endregion
+
+ [Test]
+ [MultipleAsserts]
+ public void CanCreateSparseVectorFromDenseVector()
+ {
+ var vector = (Vector)new DenseVector(_data);
+ var other = new SparseVector(vector);
+
+ Assert.AreNotSame(vector, other);
+ for (var i = 0; i < _data.Length; i++)
+ {
+ Assert.AreEqual(vector[i], other[i]);
+ }
+ }
+
+ [Test]
+ [MultipleAsserts]
+ public void CheckSparseMechanismBySettingValues()
+ {
+ var vector = new SparseVector(10000);
+
+ //Add non-zero elements
+ vector[200] = 1.5;
+ Assert.AreEqual(1.5, vector[200]);
+ Assert.AreEqual(1, vector.NonZerosCount);
+
+ vector[500] = 3.5;
+ Assert.AreEqual(3.5, vector[500]);
+ Assert.AreEqual(2, vector.NonZerosCount);
+
+ vector[800] = 5.5;
+ Assert.AreEqual(5.5, vector[800]);
+ Assert.AreEqual(3, vector.NonZerosCount);
+
+ vector[0] = 7.5;
+ Assert.AreEqual(7.5, vector[0]);
+ Assert.AreEqual(4, vector.NonZerosCount);
+
+ //Remove non-zero elements
+ vector[200] = 0;
+ Assert.AreEqual(0, vector[200]);
+ Assert.AreEqual(3, vector.NonZerosCount);
+
+ vector[500] = 0;
+ Assert.AreEqual(0, vector[500]);
+ Assert.AreEqual(2, vector.NonZerosCount);
+
+ vector[800] = 0;
+ Assert.AreEqual(0, vector[800]);
+ Assert.AreEqual(1, vector.NonZerosCount);
+
+ vector[0] = 0;
+ Assert.AreEqual(0, vector[0]);
+ Assert.AreEqual(0, vector.NonZerosCount);
+ }
+
+ [Test]
+ [MultipleAsserts]
+ public void CheckSparseMechanismByZeroMultiply()
+ {
+ var vector = new SparseVector(10000);
+
+ //Add non-zero elements
+ vector[200] = 1.5;
+ vector[500] = 3.5;
+ vector[800] = 5.5;
+ vector[0] = 7.5;
+
+ //Multiply by 0
+ vector *= 0;
+ Assert.AreEqual(0, vector[200]);
+ Assert.AreEqual(0, vector[500]);
+ Assert.AreEqual(0, vector[800]);
+ Assert.AreEqual(0, vector[0]);
+ Assert.AreEqual(0, vector.NonZerosCount);
+ }
+
+
+ [Test]
+ public void CanDotProductOfTwoSparseVectors()
+ {
+ var vectorA = new SparseVector(10000);
+ vectorA[200] = 1;
+ vectorA[500] = 3;
+ vectorA[800] = 5;
+ vectorA[100] = 7;
+ vectorA[900] = 9;
+
+ var vectorB = new SparseVector(10000);
+ vectorB[300] = 3;
+ vectorB[500] = 5;
+ vectorB[800] = 7;
+
+
+ Assert.AreEqual(50.0, vectorA.DotProduct(vectorB));
+ }
+
+ [Test]
+ public void CreateHugeSparseVector()
+ {
+ var data = new double[1000000];
+ var rnd = new Random();
+ for (int i = 0; i < 1000000; i++)
+ data[i] = rnd.Next();
+
+ var vector = new SparseVector(data);
+ }
+ }
+}
diff --git a/src/UnitTests/UnitTests.csproj b/src/UnitTests/UnitTests.csproj
index 8b4f06e8..5a65d53a 100644
--- a/src/UnitTests/UnitTests.csproj
+++ b/src/UnitTests/UnitTests.csproj
@@ -44,6 +44,7 @@
prompt
4
AllRules.ruleset
+ AnyCPU
pdbonly
@@ -120,6 +121,8 @@
+
+