// // 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 Generic; using Properties; using Storage; using Threading; /// /// A Matrix class with sparse storage. The underlying storage scheme is 3-array compressed-sparse-row (CSR) Format. /// Wikipedia - CSR. /// [Serializable] public class SparseMatrix : Matrix { readonly SparseCompressedRowMatrixStorage _storage; /// /// Gets the number of non zero elements in the matrix. /// /// The number of non zero elements. public int NonZerosCount { get { return _storage.ValueCount; } } internal SparseCompressedRowMatrixStorage Raw { get { return _storage; } } internal SparseMatrix(SparseCompressedRowMatrixStorage storage) : base(storage) { _storage = storage; } /// /// Initializes a new instance of the class. /// /// /// The number of rows. /// /// /// The number of columns. /// public SparseMatrix(int rows, int columns) : this(new SparseCompressedRowMatrixStorage(rows, columns, 0d)) { } /// /// Initializes a new instance of the class. This matrix is square with a given size. /// /// the size of the square matrix. /// /// If is less than one. /// public SparseMatrix(int order) : this(order, order) { } /// /// Initializes a new instance of the class with all entries set to a particular value. /// /// /// The number of rows. /// /// /// The number of columns. /// /// The value which we assign to each element of the matrix. public SparseMatrix(int rows, int columns, double value) : this(rows, columns) { if (value == 0.0) { return; } var rowPointers = _storage.RowPointers; var valueCount = _storage.ValueCount = rows * columns; var columnIndices = _storage.ColumnIndices = new int[valueCount]; var values = _storage.Values = new double[valueCount]; for (int i = 0, j = 0; i < values.Length; i++, j++) { // Reset column position to "0" if (j == columns) { j = 0; } values[i] = value; columnIndices[i] = j; } // Set proper row pointers for (var i = 0; i < rowPointers.Length; i++) { rowPointers[i] = ((i + 1) * columns) - columns; } } /// /// Initializes a new instance of the class from a one dimensional array. /// /// The number of rows. /// The number of columns. /// The one dimensional array to create this matrix from. This array should store the matrix in column-major order. see: http://en.wikipedia.org/wiki/Column-major_order /// If length is less than * . /// public SparseMatrix(int rows, int columns, double[] array) : this(rows, columns) { if (rows * columns > array.Length) { throw new ArgumentOutOfRangeException(Resources.ArgumentMatrixDimensions); } for (var i = 0; i < rows; i++) { for (var j = 0; j < columns; j++) { _storage.At(i, j, array[i + (j * rows)]); } } } /// /// Initializes a new instance of the class from a 2D array. /// /// The 2D array to create this matrix from. public SparseMatrix(double[,] array) : this(array.GetLength(0), array.GetLength(1)) { for (var i = 0; i < _storage.RowCount; i++) { for (var j = 0; j < _storage.ColumnCount; j++) { _storage.At(i, j, array[i, j]); } } } /// /// Initializes a new instance of the class, copying /// the values from the given matrix. /// /// The matrix to copy. public SparseMatrix(Matrix matrix) : this(matrix.RowCount, matrix.ColumnCount) { var sparseMatrix = matrix as SparseMatrix; var rows = matrix.RowCount; var columns = matrix.ColumnCount; if (sparseMatrix == null) { for (var i = 0; i < rows; i++) { for (var j = 0; j < columns; j++) { _storage.At(i, j, matrix.At(i, j)); } } } else { var matrixStorage = sparseMatrix.Raw; var valueCount = _storage.ValueCount = matrixStorage.ValueCount; _storage.ColumnIndices = new int[valueCount]; _storage.Values = new double[valueCount]; Buffer.BlockCopy(matrixStorage.Values, 0, _storage.Values, 0, valueCount * Constants.SizeOfDouble); Buffer.BlockCopy(matrixStorage.ColumnIndices, 0, _storage.ColumnIndices, 0, valueCount * Constants.SizeOfInt); Buffer.BlockCopy(matrixStorage.RowPointers, 0, _storage.RowPointers, 0, rows * Constants.SizeOfInt); } } /// /// Creates a SparseMatrix for the given number of rows and columns. /// /// /// The number of rows. /// /// /// The number of columns. /// /// /// A SparseMatrix with the given dimensions. /// public override Matrix CreateMatrix(int numberOfRows, int numberOfColumns) { return new SparseMatrix(numberOfRows, numberOfColumns); } /// /// Creates a with a the given dimension. /// /// The size of the vector. /// /// A with the given dimension. /// public override Vector CreateVector(int size) { return new SparseVector(size); } /// /// Returns a new matrix containing the lower triangle of this matrix. /// /// The lower triangle of this matrix. public override Matrix LowerTriangle() { var result = CreateMatrix(RowCount, ColumnCount); LowerTriangleImpl(result); return result; } /// /// Puts the lower triangle of this matrix into the result matrix. /// /// Where to store the lower triangle. /// If is . /// If the result matrix's dimensions are not the same as this matrix. public override void LowerTriangle(Matrix result) { if (result == null) { throw new ArgumentNullException("result"); } if (result.RowCount != RowCount || result.ColumnCount != ColumnCount) { throw DimensionsDontMatch(this, result, "result"); } if (ReferenceEquals(this, result)) { var tmp = result.CreateMatrix(result.RowCount, result.ColumnCount); LowerTriangle(tmp); tmp.CopyTo(result); } else { result.Clear(); LowerTriangleImpl(result); } } /// /// Puts the lower triangle of this matrix into the result matrix. /// /// Where to store the lower triangle. private void LowerTriangleImpl(Matrix result) { var rowPointers = _storage.RowPointers; var columnIndices = _storage.ColumnIndices; var values = _storage.Values; var valueCount = _storage.ValueCount; for (var row = 0; row < result.RowCount; row++) { var startIndex = rowPointers[row]; var endIndex = row < rowPointers.Length - 1 ? rowPointers[row + 1] : valueCount; for (var j = startIndex; j < endIndex; j++) { if (row >= columnIndices[j]) { result.At(row, columnIndices[j], values[j]); } } } } /// /// Returns a new matrix containing the upper triangle of this matrix. /// /// The upper triangle of this matrix. public override Matrix UpperTriangle() { var result = CreateMatrix(RowCount, ColumnCount); UpperTriangleImpl(result); return result; } /// /// Puts the upper triangle of this matrix into the result matrix. /// /// Where to store the lower triangle. /// If is . /// If the result matrix's dimensions are not the same as this matrix. public override void UpperTriangle(Matrix result) { if (result == null) { throw new ArgumentNullException("result"); } if (result.RowCount != RowCount || result.ColumnCount != ColumnCount) { throw DimensionsDontMatch(this, result, "result"); } if (ReferenceEquals(this, result)) { var tmp = result.CreateMatrix(result.RowCount, result.ColumnCount); UpperTriangle(tmp); tmp.CopyTo(result); } else { result.Clear(); UpperTriangleImpl(result); } } /// /// Puts the upper triangle of this matrix into the result matrix. /// /// Where to store the lower triangle. private void UpperTriangleImpl(Matrix result) { var rowPointers = _storage.RowPointers; var columnIndices = _storage.ColumnIndices; var values = _storage.Values; var valueCount = _storage.ValueCount; for (var row = 0; row < result.RowCount; row++) { var startIndex = rowPointers[row]; var endIndex = row < rowPointers.Length - 1 ? rowPointers[row + 1] : valueCount; for (var j = startIndex; j < endIndex; j++) { if (row <= columnIndices[j]) { result.At(row, columnIndices[j], values[j]); } } } } /// /// Creates a matrix that contains the values from the requested sub-matrix. /// /// The row to start copying from. /// The number of rows to copy. Must be positive. /// The column to start copying from. /// The number of columns to copy. Must be positive. /// The requested sub-matrix. /// If: is /// negative, or greater than or equal to the number of rows. /// is negative, or greater than or equal to the number /// of columns. /// (columnIndex + columnLength) >= Columns /// (rowIndex + rowLength) >= Rows /// If or /// is not positive. public override Matrix SubMatrix(int rowIndex, int rowCount, int columnIndex, int columnCount) { if (rowIndex >= RowCount || rowIndex < 0) { throw new ArgumentOutOfRangeException("rowIndex"); } if (columnIndex >= ColumnCount || columnIndex < 0) { throw new ArgumentOutOfRangeException("columnIndex"); } if (rowCount < 1) { throw new ArgumentOutOfRangeException("rowCount", Resources.ArgumentMustBePositive); } if (columnCount < 1) { throw new ArgumentOutOfRangeException("columnCount", Resources.ArgumentMustBePositive); } var colMax = columnIndex + columnCount; var rowMax = rowIndex + rowCount; if (rowMax > RowCount) { throw new ArgumentOutOfRangeException("rowCount"); } if (colMax > ColumnCount) { throw new ArgumentOutOfRangeException("columnCount"); } var result = (SparseMatrix)CreateMatrix(rowCount, columnCount); var rowPointers = _storage.RowPointers; var columnIndices = _storage.ColumnIndices; var values = _storage.Values; var valueCount = _storage.ValueCount; for (int i = rowIndex, row = 0; i < rowMax; i++, row++) { var startIndex = rowPointers[i]; var endIndex = i < rowPointers.Length - 1 ? rowPointers[i + 1] : valueCount; for (int j = startIndex; j < endIndex; j++) { // check if the column index is in the range if ((columnIndices[j] >= columnIndex) && (columnIndices[j] < columnIndex + columnCount)) { var column = columnIndices[j] - columnIndex; result._storage.At(row, column, values[j]); } } } return result; } /// /// Returns a new matrix containing the lower triangle of this matrix. The new matrix /// does not contain the diagonal elements of this matrix. /// /// The lower triangle of this matrix. public override Matrix StrictlyLowerTriangle() { var result = CreateMatrix(RowCount, ColumnCount); StrictlyLowerTriangleImpl(result); return result; } /// /// Puts the strictly lower triangle of this matrix into the result matrix. /// /// Where to store the lower triangle. /// If is . /// If the result matrix's dimensions are not the same as this matrix. public override void StrictlyLowerTriangle(Matrix result) { if (result == null) { throw new ArgumentNullException("result"); } if (result.RowCount != RowCount || result.ColumnCount != ColumnCount) { throw DimensionsDontMatch(this, result, "result"); } if (ReferenceEquals(this, result)) { var tmp = result.CreateMatrix(result.RowCount, result.ColumnCount); StrictlyLowerTriangle(tmp); tmp.CopyTo(result); } else { result.Clear(); StrictlyLowerTriangleImpl(result); } } /// /// Puts the strictly lower triangle of this matrix into the result matrix. /// /// Where to store the lower triangle. private void StrictlyLowerTriangleImpl(Matrix result) { var rowPointers = _storage.RowPointers; var columnIndices = _storage.ColumnIndices; var values = _storage.Values; var valueCount = _storage.ValueCount; for (var row = 0; row < result.RowCount; row++) { var startIndex = rowPointers[row]; var endIndex = row < rowPointers.Length - 1 ? rowPointers[row + 1] : valueCount; for (var j = startIndex; j < endIndex; j++) { if (row > columnIndices[j]) { result.At(row, columnIndices[j], values[j]); } } } } /// /// Returns a new matrix containing the upper triangle of this matrix. The new matrix /// does not contain the diagonal elements of this matrix. /// /// The upper triangle of this matrix. public override Matrix StrictlyUpperTriangle() { var result = CreateMatrix(RowCount, ColumnCount); StrictlyUpperTriangleImpl(result); return result; } /// /// Puts the strictly upper triangle of this matrix into the result matrix. /// /// Where to store the lower triangle. /// If is . /// If the result matrix's dimensions are not the same as this matrix. public override void StrictlyUpperTriangle(Matrix result) { if (result == null) { throw new ArgumentNullException("result"); } if (result.RowCount != RowCount || result.ColumnCount != ColumnCount) { throw DimensionsDontMatch(this, result, "result"); } if (ReferenceEquals(this, result)) { var tmp = result.CreateMatrix(result.RowCount, result.ColumnCount); StrictlyUpperTriangle(tmp); tmp.CopyTo(result); } else { result.Clear(); StrictlyUpperTriangleImpl(result); } } /// /// Puts the strictly upper triangle of this matrix into the result matrix. /// /// Where to store the lower triangle. private void StrictlyUpperTriangleImpl(Matrix result) { var rowPointers = _storage.RowPointers; var columnIndices = _storage.ColumnIndices; var values = _storage.Values; var valueCount = _storage.ValueCount; for (var row = 0; row < result.RowCount; row++) { var startIndex = rowPointers[row]; var endIndex = row < rowPointers.Length - 1 ? rowPointers[row + 1] : valueCount; for (var j = startIndex; j < endIndex; j++) { if (row < columnIndices[j]) { result.At(row, columnIndices[j], values[j]); } } } } /// /// Returns the matrix's elements as an array with the data laid out column-wise. /// ///
        /// 1, 2, 3
        /// 4, 5, 6  will be returned as  1, 4, 7, 2, 5, 8, 3, 6, 9
        /// 7, 8, 9
        /// 
/// An array containing the matrix's elements. public override double[] ToColumnWiseArray() { var values = _storage.Values; var ret = new double[RowCount * ColumnCount]; for (var j = 0; j < ColumnCount; j++) { for (var i = 0; i < RowCount; i++) { var index = _storage.FindItem(i, j); ret[(j * RowCount) + i] = index >= 0 ? values[index] : 0.0; } } return ret; } /// /// 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 values = _storage.Values; var hashNum = Math.Min(_storage.ValueCount, 25); int hash = 17; unchecked { for (var i = 0; i < hashNum; i++) { hash = hash*31 + values[i].GetHashCode(); } } return hash; } /// /// Returns the transpose of this matrix. /// /// The transpose of this matrix. public override Matrix Transpose() { var rowPointers = _storage.RowPointers; var columnIndices = _storage.ColumnIndices; var values = _storage.Values; var valueCount = _storage.ValueCount; var ret = new SparseMatrix(ColumnCount, RowCount); var retStorage = ret.Raw; retStorage.ColumnIndices = new int[valueCount]; retStorage.Values = new double[valueCount]; // Do an 'inverse' CopyTo iterate over the rows for (var i = 0; i < rowPointers.Length; i++) { // Get the begin / end index for the current row var startIndex = rowPointers[i]; var endIndex = i < rowPointers.Length - 1 ? rowPointers[i + 1] : valueCount; // Get the values for the current row if (startIndex == endIndex) { // Begin and end are equal. There are no values in the row, Move to the next row continue; } for (var j = startIndex; j < endIndex; j++) { retStorage.At(columnIndices[j], i, values[j]); } } return ret; } /// Calculates the Frobenius norm of this matrix. /// The Frobenius norm of this matrix. public override double FrobeniusNorm() { var transpose = (SparseMatrix)Transpose(); var aat = (this * transpose).Raw; var norm = 0d; for (var i = 0; i < aat.RowPointers.Length; i++) { // Get the begin / end index for the current row var startIndex = aat.RowPointers[i]; var endIndex = i < aat.RowPointers.Length - 1 ? aat.RowPointers[i + 1] : aat.ValueCount; // Get the values for the current row if (startIndex == endIndex) { // Begin and end are equal. There are no values in the row, Move to the next row continue; } for (var j = startIndex; j < endIndex; j++) { if (i == aat.ColumnIndices[j]) { norm += Math.Abs(aat.Values[j]); } } } return Math.Sqrt(norm); } /// Calculates the infinity norm of this matrix. /// The infinity norm of this matrix. public override double InfinityNorm() { var rowPointers = _storage.RowPointers; var values = _storage.Values; var valueCount = _storage.ValueCount; var norm = 0d; for (var i = 0; i < rowPointers.Length; i++) { // Get the begin / end index for the current row var startIndex = rowPointers[i]; var endIndex = i < rowPointers.Length - 1 ? rowPointers[i + 1] : valueCount; // Get the values for the current row if (startIndex == endIndex) { // Begin and end are equal. There are no values in the row, Move to the next row continue; } var s = 0d; for (var j = startIndex; j < endIndex; j++) { s += Math.Abs(values[j]); } norm = Math.Max(norm, s); } return norm; } /// /// Copies the requested row elements into a new . /// /// The row to copy elements from. /// The column to start copying from. /// The number of elements to copy. /// The to copy the column into. /// If the result is . /// If is negative, /// or greater than or equal to the number of columns. /// If is negative, /// or greater than or equal to the number of rows. /// If + /// is greater than or equal to the number of rows. /// If is not positive. /// If result.Count < length. public override void Row(int rowIndex, int columnIndex, int length, Vector result) { if (result == null) { throw new ArgumentNullException("result"); } if (rowIndex >= RowCount || rowIndex < 0) { throw new ArgumentOutOfRangeException("rowIndex"); } if (columnIndex >= ColumnCount || columnIndex < 0) { throw new ArgumentOutOfRangeException("columnIndex"); } if (columnIndex + length > ColumnCount) { throw new ArgumentOutOfRangeException("length"); } if (length < 1) { throw new ArgumentOutOfRangeException("length", Resources.ArgumentMustBePositive); } if (result.Count < length) { throw new ArgumentOutOfRangeException("result", Resources.ArgumentVectorsSameLength); } var rowPointers = _storage.RowPointers; var values = _storage.Values; var valueCount = _storage.ValueCount; // Determine bounds in columnIndices array where this item should be searched (using rowIndex) var startIndex = rowPointers[rowIndex]; var endIndex = rowIndex < rowPointers.Length - 1 ? rowPointers[rowIndex + 1] : valueCount; if (startIndex == endIndex) { result.Clear(); } else { // If there are non-zero elements use base class implementation for (int i = columnIndex, j = 0; i < columnIndex + length; i++, j++) { // Copy code from At(row, column) to avoid unnecessary lock var index = _storage.FindItem(rowIndex, i); result[j] = index >= 0 ? values[index] : 0d; } } } /// /// Diagonally stacks this matrix on top of the given matrix and places the combined matrix into the result matrix. /// /// The lower, right matrix. /// The combined matrix /// If lower is . /// If the result matrix is . /// If the result matrix's dimensions are not (Rows + lower.rows) x (Columns + lower.Columns). public override void DiagonalStack(Matrix lower, Matrix result) { var lowerSparseMatrix = lower as SparseMatrix; var resultSparseMatrix = result as SparseMatrix; if ((lowerSparseMatrix == null) || (resultSparseMatrix == null)) { base.DiagonalStack(lower, result); } else { var resultStorage = resultSparseMatrix.Raw; var lowerStorage = lowerSparseMatrix.Raw; if (resultStorage.RowCount != RowCount + lowerStorage.RowCount || resultStorage.ColumnCount != ColumnCount + lowerSparseMatrix.ColumnCount) { throw DimensionsDontMatch(this, lowerSparseMatrix, resultSparseMatrix); } resultStorage.ValueCount = _storage.ValueCount + lowerStorage.ValueCount; resultStorage.Values = new double[resultStorage.ValueCount]; resultStorage.ColumnIndices = new int[resultStorage.ValueCount]; Array.Copy(_storage.Values, 0, resultStorage.Values, 0, _storage.ValueCount); Array.Copy(lowerStorage.Values, 0, resultStorage.Values, _storage.ValueCount, lowerStorage.ValueCount); Array.Copy(_storage.ColumnIndices, 0, resultStorage.ColumnIndices, 0, _storage.ValueCount); Array.Copy(_storage.RowPointers, 0, resultStorage.RowPointers, 0, RowCount); // Copy and adjust lower column indices and rowIndex for (int i = _storage.ValueCount, j = 0; i < resultStorage.ValueCount; i++, j++) { resultStorage.ColumnIndices[i] = lowerStorage.ColumnIndices[j] + ColumnCount; } for (int i = RowCount, j = 0; i < resultStorage.RowCount; i++, j++) { resultStorage.RowPointers[i] = lowerStorage.RowPointers[j] + _storage.ValueCount; } } } #region Static constructors for special matrices. /// /// Initializes a square with all zero's except for ones on the diagonal. /// /// the size of the square matrix. /// Identity SparseMatrix /// /// If is less than one. /// public static SparseMatrix Identity(int order) { var m = new SparseMatrix(order); var mStorage = m.Raw; mStorage.ValueCount = order; mStorage.Values = new double[order]; mStorage.ColumnIndices = new int[order]; for (var i = 0; i < order; i++) { mStorage.Values[i] = 1d; mStorage.ColumnIndices[i] = i; mStorage.RowPointers[i] = i; } return m; } #endregion /// /// Indicates whether the current object is equal to another object of the same type. /// /// /// An object to compare with this object. /// /// /// true if the current object is equal to the parameter; otherwise, false. /// public override bool Equals(Matrix other) { if (other == null) { return false; } if (ColumnCount != other.ColumnCount || RowCount != other.RowCount) { return false; } // Accept if the argument is the same object as this. if (ReferenceEquals(this, other)) { return true; } var sparseMatrix = other as SparseMatrix; if (sparseMatrix == null) { return base.Equals(other); } var otherStorage = sparseMatrix.Raw; if (_storage.ValueCount != otherStorage.ValueCount) { return false; } // If all else fails, perform element wise comparison. for (var index = 0; index < _storage.ValueCount; index++) { if (!_storage.Values[index].AlmostEqual(otherStorage.Values[index]) || _storage.ColumnIndices[index] != otherStorage.ColumnIndices[index]) { return false; } } return true; } /// /// Adds another matrix to this matrix. /// /// The matrix to add to this matrix. /// The matrix to store the result of the addition. /// If the other matrix is . /// If the two matrices don't have the same dimensions. protected override void DoAdd(Matrix other, Matrix result) { var sparseOther = other as SparseMatrix; var sparseResult = result as SparseMatrix; if (sparseOther == null || sparseResult == null) { base.DoAdd(other, result); return; } if (ReferenceEquals(this, other)) { if (!ReferenceEquals(this, result)) { CopyTo(result); } Control.LinearAlgebraProvider.ScaleArray(2.0, _storage.Values, _storage.Values); return; } SparseMatrix left; if (ReferenceEquals(sparseOther, sparseResult)) { left = this; } else if (ReferenceEquals(this, sparseResult)) { left = sparseOther; } else { CopyTo(sparseResult); left = sparseOther; } var leftStorage = left.Raw; for (var i = 0; i < leftStorage.RowCount; i++) { // Get the begin / end index for the current row var startIndex = leftStorage.RowPointers[i]; var endIndex = i < leftStorage.RowPointers.Length - 1 ? leftStorage.RowPointers[i + 1] : leftStorage.ValueCount; for (var j = startIndex; j < endIndex; j++) { var columnIndex = leftStorage.ColumnIndices[j]; var resVal = leftStorage.Values[j] + result.At(i, columnIndex); result.At(i, columnIndex, resVal); } } } /// /// Subtracts another matrix from this matrix. /// /// The matrix to subtract to this matrix. /// The matrix to store the result of subtraction. /// If the other matrix is . /// If the two matrices don't have the same dimensions. protected override void DoSubtract(Matrix other, Matrix result) { var sparseOther = other as SparseMatrix; var sparseResult = result as SparseMatrix; if (sparseOther == null || sparseResult == null) { base.DoSubtract(other, result); return; } if (ReferenceEquals(this, other)) { result.Clear(); return; } var otherStorage = sparseOther.Raw; if (ReferenceEquals(this, sparseResult)) { for (var i = 0; i < otherStorage.RowCount; i++) { // Get the begin / end index for the current row var startIndex = otherStorage.RowPointers[i]; var endIndex = i < otherStorage.RowPointers.Length - 1 ? otherStorage.RowPointers[i + 1] : otherStorage.ValueCount; for (var j = startIndex; j < endIndex; j++) { var columnIndex = otherStorage.ColumnIndices[j]; var resVal = sparseResult.At(i, columnIndex) - otherStorage.Values[j]; result.At(i, columnIndex, resVal); } } } else { if (!ReferenceEquals(sparseOther, sparseResult)) { sparseOther.CopyTo(sparseResult); } sparseResult.Negate(sparseResult); var rowPointers = _storage.RowPointers; var columnIndices = _storage.ColumnIndices; var values = _storage.Values; var valueCount = _storage.ValueCount; for (var i = 0; i < RowCount; i++) { // Get the begin / end index for the current row var startIndex = rowPointers[i]; var endIndex = i < rowPointers.Length - 1 ? rowPointers[i + 1] : valueCount; for (var j = startIndex; j < endIndex; j++) { var columnIndex = columnIndices[j]; var resVal = sparseResult.At(i, columnIndex) + values[j]; result.At(i, columnIndex, resVal); } } } } /// /// Multiplies each element of the matrix by a scalar and places results into the result matrix. /// /// The scalar to multiply the matrix with. /// The matrix to store the result of the multiplication. protected override void DoMultiply(double scalar, Matrix result) { if (scalar == 1.0) { CopyTo(result); return; } if (scalar == 0.0 || _storage.ValueCount == 0) { result.Clear(); return; } var sparseResult = result as SparseMatrix; if (sparseResult == null) { result.Clear(); var rowPointers = _storage.RowPointers; var columnIndices = _storage.ColumnIndices; var values = _storage.Values; for (var row = 0; row < RowCount; row++) { var start = rowPointers[row]; var end = rowPointers[row + 1]; if (start == end) { continue; } for (var index = start; index < end; index++) { var column = columnIndices[index]; result.At(row, column, values[index] * scalar); } } } else { if (!ReferenceEquals(this, result)) { CopyTo(sparseResult); } CommonParallel.For(0, _storage.ValueCount, index => sparseResult.Raw.Values[index] *= scalar); } } /// /// Multiplies this matrix with another matrix and places the results into the result matrix. /// /// The matrix to multiply with. /// The result of the multiplication. protected override void DoMultiply(Matrix other, Matrix result) { result.Clear(); var columnVector = new DenseVector(other.RowCount); var rowPointers = _storage.RowPointers; var columnIndices = _storage.ColumnIndices; var values = _storage.Values; var valueCount = _storage.ValueCount; for (var row = 0; row < RowCount; row++) { // Get the begin / end index for the current row var startIndex = rowPointers[row]; var endIndex = row < rowPointers.Length - 1 ? rowPointers[row + 1] : valueCount; if (startIndex == endIndex) { continue; } for (var column = 0; column < other.ColumnCount; column++) { // Multiply row of matrix A on column of matrix B other.Column(column, columnVector); var sum = 0d; for (var index = startIndex; index < endIndex; index++) { sum += values[index] * columnVector[columnIndices[index]]; } result.At(row, column, sum); } } } /// /// Multiplies this matrix with a vector and places the results into the result vector. /// /// The vector to multiply with. /// The result of the multiplication. protected override void DoMultiply(Vector rightSide, Vector result) { var rowPointers = _storage.RowPointers; var columnIndices = _storage.ColumnIndices; var values = _storage.Values; var valueCount = _storage.ValueCount; for (var row = 0; row < RowCount; row++) { // Get the begin / end index for the current row var startIndex = rowPointers[row]; var endIndex = row < rowPointers.Length - 1 ? rowPointers[row + 1] : valueCount; if (startIndex == endIndex) { continue; } var sum = 0d; for (var index = startIndex; index < endIndex; index++) { sum += values[index] * rightSide[columnIndices[index]]; } result[row] = sum; } } /// /// Multiplies this matrix with transpose of another matrix and places the results into the result matrix. /// /// The matrix to multiply with. /// The result of the multiplication. protected override void DoTransposeAndMultiply(Matrix other, Matrix result) { var otherSparse = other as SparseMatrix; var resultSparse = result as SparseMatrix; if (otherSparse == null || resultSparse == null) { base.DoTransposeAndMultiply(other, result); return; } resultSparse.Clear(); var rowPointers = _storage.RowPointers; var values = _storage.Values; var valueCount = _storage.ValueCount; var otherStorage = otherSparse.Raw; for (var j = 0; j < RowCount; j++) { // Get the begin / end index for the row var startIndexOther = otherStorage.RowPointers[j]; var endIndexOther = j < otherStorage.RowPointers.Length - 1 ? otherStorage.RowPointers[j + 1] : otherStorage.ValueCount; if (startIndexOther == endIndexOther) { continue; } for (var i = 0; i < RowCount; i++) { // Multiply row of matrix A on row of matrix B // Get the begin / end index for the row var startIndexThis = rowPointers[i]; var endIndexThis = i < rowPointers.Length - 1 ? rowPointers[i + 1] : valueCount; if (startIndexThis == endIndexThis) { continue; } var sum = 0d; for (var index = startIndexOther; index < endIndexOther; index++) { var ind = _storage.FindItem(i, otherStorage.ColumnIndices[index]); if (ind >= 0) { sum += otherStorage.Values[index]*values[ind]; } } resultSparse.Raw.At(i, j, sum + result.At(i, j)); } } } /// /// Negate each element of this matrix and place the results into the result matrix. /// /// The result of the negation. protected override void DoNegate(Matrix result) { CopyTo(result); DoMultiply(-1, result); } /// /// Pointwise multiplies this matrix with another matrix and stores the result into the result matrix. /// /// The matrix to pointwise multiply with this one. /// The matrix to store the result of the pointwise multiplication. protected override void DoPointwiseMultiply(Matrix other, Matrix result) { result.Clear(); var rowPointers = _storage.RowPointers; var columnIndices = _storage.ColumnIndices; var values = _storage.Values; var valueCount = _storage.ValueCount; for (var i = 0; i < other.RowCount; i++) { // Get the begin / end index for the current row var startIndex = rowPointers[i]; var endIndex = i < rowPointers.Length - 1 ? rowPointers[i + 1] : valueCount; for (var j = startIndex; j < endIndex; j++) { var resVal = values[j] * other.At(i, columnIndices[j]); if (resVal != 0d) { result.At(i, columnIndices[j], resVal); } } } } /// /// Pointwise divide this matrix by another matrix and stores the result into the result matrix. /// /// The matrix to pointwise divide this one by. /// The matrix to store the result of the pointwise division. protected override void DoPointwiseDivide(Matrix other, Matrix result) { result.Clear(); var rowPointers = _storage.RowPointers; var columnIndices = _storage.ColumnIndices; var values = _storage.Values; var valueCount = _storage.ValueCount; for (var i = 0; i < other.RowCount; i++) { // Get the begin / end index for the current row var startIndex = rowPointers[i]; var endIndex = i < rowPointers.Length - 1 ? rowPointers[i + 1] : valueCount; for (var j = startIndex; j < endIndex; j++) { var resVal = values[j] / other.At(i, columnIndices[j]); if (resVal != 0d) { result.At(i, columnIndices[j], resVal); } } } } /// /// Computes the modulus for each element of the matrix. /// /// The divisor to use. /// Matrix to store the results in. protected override void DoModulus(double divisor, Matrix result) { var sparseResult = result as SparseMatrix; if (sparseResult == null) { base.DoModulus(divisor, result); return; } if (!ReferenceEquals(this, result)) { CopyTo(result); } var resultStorage = sparseResult.Raw; for (var index = 0; index < resultStorage.Values.Length; index++) { resultStorage.Values[index] %= divisor; } } /// /// Iterates throw each element in the matrix (row-wise). /// /// The value at the current iteration along with its position (row, column, value). public override IEnumerable> IndexedEnumerator() { var rowPointers = _storage.RowPointers; var columnIndices = _storage.ColumnIndices; var values = _storage.Values; var valueCount = _storage.ValueCount; for (var row = 0; row < RowCount - 1; row++) { var start = rowPointers[row]; var end = rowPointers[row + 1]; if (start == end) { continue; } for (var index = start; index < end; index++) { yield return new Tuple(row, columnIndices[index], values[index]); } } var lastRow = rowPointers.Length - 1; if (rowPointers[lastRow] < valueCount) { for (var index = rowPointers[lastRow]; index < valueCount; index++) { yield return new Tuple(lastRow, columnIndices[index], values[index]); } } } /// /// Gets a value indicating whether this matrix is symmetric. /// public override bool IsSymmetric { get { if (RowCount != ColumnCount) { return false; } // todo: we might be able to speed this up by caching one half of the matrix var rowPointers = _storage.RowPointers; for (var row = 0; row < RowCount - 1; row++) { var start = rowPointers[row]; var end = rowPointers[row + 1]; if (start == end) { continue; } if (!CheckIfOppositesAreEqual(start, end, row)) { return false; } } var lastRow = rowPointers.Length - 1; if (rowPointers[lastRow] < _storage.ValueCount) { if (!CheckIfOppositesAreEqual(rowPointers[lastRow], _storage.ValueCount, lastRow)) { return false; } } return true; } } /// /// Checks if opposites in a range are equal. /// /// The start of the range. /// The end of the range. /// The row the row to check. /// If the values are equal or not. private bool CheckIfOppositesAreEqual(int start, int end, int row) { var columnIndices = _storage.ColumnIndices; var values = _storage.Values; for (var index = start; index < end; index++) { var column = columnIndices[index]; var opposite = At(column, row); if (!values[index].Equals(opposite)) { return false; } } return true; } /// /// Adds two matrices together and returns the results. /// /// This operator will allocate new memory for the result. It will /// choose the representation of either or depending on which /// is denser. /// The left matrix to add. /// The right matrix to add. /// The result of the addition. /// If and don't have the same dimensions. /// If or is . public static SparseMatrix operator +(SparseMatrix leftSide, SparseMatrix rightSide) { if (rightSide == null) { throw new ArgumentNullException("rightSide"); } if (leftSide == null) { throw new ArgumentNullException("leftSide"); } if (leftSide.RowCount != rightSide.RowCount || leftSide.ColumnCount != rightSide.ColumnCount) { throw DimensionsDontMatch(leftSide, rightSide); } return (SparseMatrix)leftSide.Add(rightSide); } /// /// Returns a Matrix containing the same values of . /// /// The matrix to get the values from. /// A matrix containing a the same values as . /// If is . public static SparseMatrix operator +(SparseMatrix rightSide) { if (rightSide == null) { throw new ArgumentNullException("rightSide"); } return (SparseMatrix)rightSide.Clone(); } /// /// Subtracts two matrices together and returns the results. /// /// This operator will allocate new memory for the result. It will /// choose the representation of either or depending on which /// is denser. /// The left matrix to subtract. /// The right matrix to subtract. /// The result of the addition. /// If and don't have the same dimensions. /// If or is . public static SparseMatrix operator -(SparseMatrix leftSide, SparseMatrix rightSide) { if (rightSide == null) { throw new ArgumentNullException("rightSide"); } if (leftSide == null) { throw new ArgumentNullException("leftSide"); } if (leftSide.RowCount != rightSide.RowCount || leftSide.ColumnCount != rightSide.ColumnCount) { throw DimensionsDontMatch(leftSide, rightSide); } return (SparseMatrix)leftSide.Subtract(rightSide); } /// /// Negates each element of the matrix. /// /// The matrix to negate. /// A matrix containing the negated values. /// If is . public static SparseMatrix operator -(SparseMatrix rightSide) { if (rightSide == null) { throw new ArgumentNullException("rightSide"); } return (SparseMatrix)rightSide.Negate(); } /// /// Multiplies a Matrix by a constant and returns the result. /// /// The matrix to multiply. /// The constant to multiply the matrix by. /// The result of the multiplication. /// If is . public static SparseMatrix operator *(SparseMatrix leftSide, double rightSide) { if (leftSide == null) { throw new ArgumentNullException("leftSide"); } return (SparseMatrix)leftSide.Multiply(rightSide); } /// /// Multiplies a Matrix by a constant and returns the result. /// /// The matrix to multiply. /// The constant to multiply the matrix by. /// The result of the multiplication. /// If is . public static SparseMatrix operator *(double leftSide, SparseMatrix rightSide) { if (rightSide == null) { throw new ArgumentNullException("rightSide"); } return (SparseMatrix)rightSide.Multiply(leftSide); } /// /// Multiplies two matrices. /// /// This operator will allocate new memory for the result. It will /// choose the representation of either or depending on which /// is denser. /// The left matrix to multiply. /// The right matrix to multiply. /// The result of multiplication. /// If or is . /// If the dimensions of or don't conform. public static SparseMatrix operator *(SparseMatrix leftSide, SparseMatrix rightSide) { if (leftSide == null) { throw new ArgumentNullException("leftSide"); } if (rightSide == null) { throw new ArgumentNullException("rightSide"); } if (leftSide.ColumnCount != rightSide.RowCount) { throw DimensionsDontMatch(leftSide, rightSide); } return (SparseMatrix)leftSide.Multiply(rightSide); } /// /// Multiplies a Matrix and a Vector. /// /// The matrix to multiply. /// The vector to multiply. /// The result of multiplication. /// If or is . public static SparseVector operator *(SparseMatrix leftSide, SparseVector rightSide) { if (leftSide == null) { throw new ArgumentNullException("leftSide"); } return (SparseVector)leftSide.Multiply(rightSide); } /// /// Multiplies a Vector and a Matrix. /// /// The vector to multiply. /// The matrix to multiply. /// The result of multiplication. /// If or is . public static SparseVector operator *(SparseVector leftSide, SparseMatrix rightSide) { if (rightSide == null) { throw new ArgumentNullException("rightSide"); } return (SparseVector)rightSide.LeftMultiply(leftSide); } /// /// Multiplies a Matrix by a constant and returns the result. /// /// The matrix to multiply. /// The constant to multiply the matrix by. /// The result of the multiplication. /// If is . public static SparseMatrix operator %(SparseMatrix leftSide, double rightSide) { if (leftSide == null) { throw new ArgumentNullException("leftSide"); } return (SparseMatrix)leftSide.Modulus(rightSide); } } }