// // Math.NET Numerics, part of the Math.NET Project // http://numerics.mathdotnet.com // http://github.com/mathnet/mathnet-numerics // // Copyright (c) 2009-2013 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. // using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using IStation.Numerics.LinearAlgebra.Storage; using IStation.Numerics.Providers.LinearAlgebra; namespace IStation.Numerics.LinearAlgebra.Double { /// /// A Matrix with sparse storage, intended for very large matrices where most of the cells are zero. /// The underlying storage scheme is 3-array compressed-sparse-row (CSR) Format. /// Wikipedia - CSR. /// [Serializable] [DebuggerDisplay("SparseMatrix {RowCount}x{ColumnCount}-Double {NonZerosCount}-NonZero")] 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 => _storage.ValueCount; /// /// Create a new sparse matrix straight from an initialized matrix storage instance. /// The storage is used directly without copying. /// Intended for advanced scenarios where you're working directly with /// storage for performance or interop reasons. /// public SparseMatrix(SparseCompressedRowMatrixStorage storage) : base(storage) { _storage = storage; } /// /// Create a new square sparse matrix with the given number of rows and columns. /// All cells of the matrix will be initialized to zero. /// /// If the order is less than one. public SparseMatrix(int order) : this(order, order) { } /// /// Create a new sparse matrix with the given number of rows and columns. /// All cells of the matrix will be initialized to zero. /// /// If the row or column count is less than one. public SparseMatrix(int rows, int columns) : this(new SparseCompressedRowMatrixStorage(rows, columns)) { } /// /// Create a new sparse matrix as a copy of the given other matrix. /// This new matrix will be independent from the other matrix. /// A new memory block will be allocated for storing the matrix. /// public static SparseMatrix OfMatrix(Matrix matrix) { return new SparseMatrix(SparseCompressedRowMatrixStorage.OfMatrix(matrix.Storage)); } /// /// Create a new sparse matrix as a copy of the given two-dimensional array. /// This new matrix will be independent from the provided array. /// A new memory block will be allocated for storing the matrix. /// public static SparseMatrix OfArray(double[,] array) { return new SparseMatrix(SparseCompressedRowMatrixStorage.OfArray(array)); } /// /// Create a new sparse matrix as a copy of the given indexed enumerable. /// Keys must be provided at most once, zero is assumed if a key is omitted. /// This new matrix will be independent from the enumerable. /// A new memory block will be allocated for storing the matrix. /// public static SparseMatrix OfIndexed(int rows, int columns, IEnumerable> enumerable) { return new SparseMatrix(SparseCompressedRowMatrixStorage.OfIndexedEnumerable(rows, columns, enumerable)); } /// /// Create a new sparse matrix as a copy of the given enumerable. /// The enumerable is assumed to be in row-major order (row by row). /// This new matrix will be independent from the enumerable. /// A new memory block will be allocated for storing the vector. /// /// public static SparseMatrix OfRowMajor(int rows, int columns, IEnumerable rowMajor) { return new SparseMatrix(SparseCompressedRowMatrixStorage.OfRowMajorEnumerable(rows, columns, rowMajor)); } /// /// Create a new sparse matrix with the given number of rows and columns as a copy of the given array. /// The array is assumed to be in column-major order (column by column). /// This new matrix will be independent from the provided array. /// A new memory block will be allocated for storing the matrix. /// /// public static SparseMatrix OfColumnMajor(int rows, int columns, IList columnMajor) { return new SparseMatrix(SparseCompressedRowMatrixStorage.OfColumnMajorList(rows, columns, columnMajor)); } /// /// Create a new sparse matrix as a copy of the given enumerable of enumerable columns. /// Each enumerable in the master enumerable specifies a column. /// This new matrix will be independent from the enumerables. /// A new memory block will be allocated for storing the matrix. /// public static SparseMatrix OfColumns(IEnumerable> data) { return OfColumnArrays(data.Select(v => v.ToArray()).ToArray()); } /// /// Create a new sparse matrix as a copy of the given enumerable of enumerable columns. /// Each enumerable in the master enumerable specifies a column. /// This new matrix will be independent from the enumerables. /// A new memory block will be allocated for storing the matrix. /// public static SparseMatrix OfColumns(int rows, int columns, IEnumerable> data) { return new SparseMatrix(SparseCompressedRowMatrixStorage.OfColumnEnumerables(rows, columns, data)); } /// /// Create a new sparse matrix as a copy of the given column arrays. /// This new matrix will be independent from the arrays. /// A new memory block will be allocated for storing the matrix. /// public static SparseMatrix OfColumnArrays(params double[][] columns) { return new SparseMatrix(SparseCompressedRowMatrixStorage.OfColumnArrays(columns)); } /// /// Create a new sparse matrix as a copy of the given column arrays. /// This new matrix will be independent from the arrays. /// A new memory block will be allocated for storing the matrix. /// public static SparseMatrix OfColumnArrays(IEnumerable columns) { return new SparseMatrix(SparseCompressedRowMatrixStorage.OfColumnArrays((columns as double[][]) ?? columns.ToArray())); } /// /// Create a new sparse matrix as a copy of the given column vectors. /// This new matrix will be independent from the vectors. /// A new memory block will be allocated for storing the matrix. /// public static SparseMatrix OfColumnVectors(params Vector[] columns) { var storage = new VectorStorage[columns.Length]; for (int i = 0; i < columns.Length; i++) { storage[i] = columns[i].Storage; } return new SparseMatrix(SparseCompressedRowMatrixStorage.OfColumnVectors(storage)); } /// /// Create a new sparse matrix as a copy of the given column vectors. /// This new matrix will be independent from the vectors. /// A new memory block will be allocated for storing the matrix. /// public static SparseMatrix OfColumnVectors(IEnumerable> columns) { return new SparseMatrix(SparseCompressedRowMatrixStorage.OfColumnVectors(columns.Select(c => c.Storage).ToArray())); } /// /// Create a new sparse matrix as a copy of the given enumerable of enumerable rows. /// Each enumerable in the master enumerable specifies a row. /// This new matrix will be independent from the enumerables. /// A new memory block will be allocated for storing the matrix. /// public static SparseMatrix OfRows(IEnumerable> data) { return OfRowArrays(data.Select(v => v.ToArray()).ToArray()); } /// /// Create a new sparse matrix as a copy of the given enumerable of enumerable rows. /// Each enumerable in the master enumerable specifies a row. /// This new matrix will be independent from the enumerables. /// A new memory block will be allocated for storing the matrix. /// public static SparseMatrix OfRows(int rows, int columns, IEnumerable> data) { return new SparseMatrix(SparseCompressedRowMatrixStorage.OfRowEnumerables(rows, columns, data)); } /// /// Create a new sparse matrix as a copy of the given row arrays. /// This new matrix will be independent from the arrays. /// A new memory block will be allocated for storing the matrix. /// public static SparseMatrix OfRowArrays(params double[][] rows) { return new SparseMatrix(SparseCompressedRowMatrixStorage.OfRowArrays(rows)); } /// /// Create a new sparse matrix as a copy of the given row arrays. /// This new matrix will be independent from the arrays. /// A new memory block will be allocated for storing the matrix. /// public static SparseMatrix OfRowArrays(IEnumerable rows) { return new SparseMatrix(SparseCompressedRowMatrixStorage.OfRowArrays((rows as double[][]) ?? rows.ToArray())); } /// /// Create a new sparse matrix as a copy of the given row vectors. /// This new matrix will be independent from the vectors. /// A new memory block will be allocated for storing the matrix. /// public static SparseMatrix OfRowVectors(params Vector[] rows) { var storage = new VectorStorage[rows.Length]; for (int i = 0; i < rows.Length; i++) { storage[i] = rows[i].Storage; } return new SparseMatrix(SparseCompressedRowMatrixStorage.OfRowVectors(storage)); } /// /// Create a new sparse matrix as a copy of the given row vectors. /// This new matrix will be independent from the vectors. /// A new memory block will be allocated for storing the matrix. /// public static SparseMatrix OfRowVectors(IEnumerable> rows) { return new SparseMatrix(SparseCompressedRowMatrixStorage.OfRowVectors(rows.Select(r => r.Storage).ToArray())); } /// /// Create a new sparse matrix with the diagonal as a copy of the given vector. /// This new matrix will be independent from the vector. /// A new memory block will be allocated for storing the matrix. /// public static SparseMatrix OfDiagonalVector(Vector diagonal) { var m = new SparseMatrix(diagonal.Count, diagonal.Count); m.SetDiagonal(diagonal); return m; } /// /// Create a new sparse matrix with the diagonal as a copy of the given vector. /// This new matrix will be independent from the vector. /// A new memory block will be allocated for storing the matrix. /// public static SparseMatrix OfDiagonalVector(int rows, int columns, Vector diagonal) { var m = new SparseMatrix(rows, columns); m.SetDiagonal(diagonal); return m; } /// /// Create a new sparse matrix with the diagonal as a copy of the given array. /// This new matrix will be independent from the array. /// A new memory block will be allocated for storing the matrix. /// public static SparseMatrix OfDiagonalArray(double[] diagonal) { var m = new SparseMatrix(diagonal.Length, diagonal.Length); m.SetDiagonal(diagonal); return m; } /// /// Create a new sparse matrix with the diagonal as a copy of the given array. /// This new matrix will be independent from the array. /// A new memory block will be allocated for storing the matrix. /// public static SparseMatrix OfDiagonalArray(int rows, int columns, double[] diagonal) { var m = new SparseMatrix(rows, columns); m.SetDiagonal(diagonal); return m; } /// /// Create a new sparse matrix and initialize each value to the same provided value. /// public static SparseMatrix Create(int rows, int columns, double value) { if (value == 0d) return new SparseMatrix(rows, columns); return new SparseMatrix(SparseCompressedRowMatrixStorage.OfValue(rows, columns, value)); } /// /// Create a new sparse matrix and initialize each value using the provided init function. /// public static SparseMatrix Create(int rows, int columns, Func init) { return new SparseMatrix(SparseCompressedRowMatrixStorage.OfInit(rows, columns, init)); } /// /// Create a new diagonal sparse matrix and initialize each diagonal value to the same provided value. /// public static SparseMatrix CreateDiagonal(int rows, int columns, double value) { if (value == 0d) return new SparseMatrix(rows, columns); return new SparseMatrix(SparseCompressedRowMatrixStorage.OfDiagonalInit(rows, columns, i => value)); } /// /// Create a new diagonal sparse matrix and initialize each diagonal value using the provided init function. /// public static SparseMatrix CreateDiagonal(int rows, int columns, Func init) { return new SparseMatrix(SparseCompressedRowMatrixStorage.OfDiagonalInit(rows, columns, init)); } /// /// Create a new square sparse identity matrix where each diagonal value is set to One. /// public static SparseMatrix CreateIdentity(int order) { return new SparseMatrix(SparseCompressedRowMatrixStorage.OfDiagonalInit(order, order, i => One)); } /// /// Returns a new matrix containing the lower triangle of this matrix. /// /// The lower triangle of this matrix. public override Matrix LowerTriangle() { var result = Build.SameAs(this); 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(nameof(result)); } if (result.RowCount != RowCount || result.ColumnCount != ColumnCount) { throw DimensionsDontMatch(this, result, "result"); } if (ReferenceEquals(this, result)) { var tmp = Build.SameAs(result); 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; for (var row = 0; row < result.RowCount; row++) { var endIndex = rowPointers[row + 1]; for (var j = rowPointers[row]; 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 = Build.SameAs(this); 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(nameof(result)); } if (result.RowCount != RowCount || result.ColumnCount != ColumnCount) { throw DimensionsDontMatch(this, result, "result"); } if (ReferenceEquals(this, result)) { var tmp = Build.SameAs(result); 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; for (var row = 0; row < result.RowCount; row++) { var endIndex = rowPointers[row + 1]; for (var j = rowPointers[row]; j < endIndex; j++) { if (row <= columnIndices[j]) { result.At(row, columnIndices[j], values[j]); } } } } /// /// 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 = Build.SameAs(this); 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(nameof(result)); } if (result.RowCount != RowCount || result.ColumnCount != ColumnCount) { throw DimensionsDontMatch(this, result, "result"); } if (ReferenceEquals(this, result)) { var tmp = Build.SameAs(result); 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; for (var row = 0; row < result.RowCount; row++) { var endIndex = rowPointers[row + 1]; for (var j = rowPointers[row]; 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 = Build.SameAs(this); 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(nameof(result)); } if (result.RowCount != RowCount || result.ColumnCount != ColumnCount) { throw DimensionsDontMatch(this, result, "result"); } if (ReferenceEquals(this, result)) { var tmp = Build.SameAs(result); 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; for (var row = 0; row < result.RowCount; row++) { var endIndex = rowPointers[row + 1]; for (var j = rowPointers[row]; j < endIndex; j++) { if (row < columnIndices[j]) { result.At(row, columnIndices[j], values[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); } /// Calculates the induced infinity norm of this matrix. /// The maximum absolute row sum of the matrix. public override double InfinityNorm() { var rowPointers = _storage.RowPointers; var values = _storage.Values; var norm = 0d; for (var i = 0; i < RowCount; i++) { var startIndex = rowPointers[i]; var endIndex = rowPointers[i + 1]; 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; } /// Calculates the entry-wise Frobenius norm of this matrix. /// The square root of the sum of the squared values. public override double FrobeniusNorm() { var aat = (SparseCompressedRowMatrixStorage) (this*Transpose()).Storage; var norm = 0d; for (var i = 0; i < aat.RowCount; i++) { var startIndex = aat.RowPointers[i]; var endIndex = aat.RowPointers[i + 1]; 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); } /// /// 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) { if (other is SparseMatrix sparseOther && result is SparseMatrix sparseResult) { if (ReferenceEquals(this, other)) { if (!ReferenceEquals(this, result)) { CopyTo(result); } LinearAlgebraControl.Provider.ScaleArray(2.0, sparseResult._storage.Values, sparseResult._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._storage; for (var i = 0; i < leftStorage.RowCount; i++) { var endIndex = leftStorage.RowPointers[i + 1]; for (var j = leftStorage.RowPointers[i]; j < endIndex; j++) { var columnIndex = leftStorage.ColumnIndices[j]; var resVal = leftStorage.Values[j] + result.At(i, columnIndex); result.At(i, columnIndex, resVal); } } } else { base.DoAdd(other, result); } } /// /// 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) { if (other is SparseMatrix sparseOther && result is SparseMatrix sparseResult) { if (ReferenceEquals(this, other)) { result.Clear(); return; } var otherStorage = sparseOther._storage; if (ReferenceEquals(this, sparseResult)) { for (var i = 0; i < otherStorage.RowCount; i++) { var endIndex = otherStorage.RowPointers[i + 1]; for (var j = otherStorage.RowPointers[i]; 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; for (var i = 0; i < RowCount; i++) { var endIndex = rowPointers[i + 1]; for (var j = rowPointers[i]; j < endIndex; j++) { var columnIndex = columnIndices[j]; var resVal = sparseResult.At(i, columnIndex) + values[j]; result.At(i, columnIndex, resVal); } } } } else { base.DoSubtract(other, result); } } /// /// 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 || NonZerosCount == 0) { result.Clear(); return; } if (result is SparseMatrix sparseResult) { if (!ReferenceEquals(this, result)) { CopyTo(sparseResult); } LinearAlgebraControl.Provider.ScaleArray(scalar, sparseResult._storage.Values, sparseResult._storage.Values); } else { 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); } } } } /// /// 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) { var sparseOther = other as SparseMatrix; var sparseResult = result as SparseMatrix; if (sparseOther != null && sparseResult != null) { DoMultiplySparse(sparseOther, sparseResult); return; } if (other.Storage is DiagonalMatrixStorage diagonalOther && sparseResult != null) { var diagonal = diagonalOther.Data; if (other.ColumnCount == other.RowCount) { Storage.MapIndexedTo(result.Storage, (i, j, x) => x*diagonal[j], Zeros.AllowSkip, ExistingData.Clear); } else { result.Storage.Clear(); Storage.MapSubMatrixIndexedTo(result.Storage, (i, j, x) => x*diagonal[j], 0, 0, RowCount, 0, 0, Math.Min(ColumnCount, other.ColumnCount), Zeros.AllowSkip, ExistingData.AssumeZeros); } return; } result.Clear(); var rowPointers = _storage.RowPointers; var columnIndices = _storage.ColumnIndices; var values = _storage.Values; if (other.Storage is DenseColumnMajorMatrixStorage denseOther) { // in this case we can directly address the underlying data-array for (var row = 0; row < RowCount; row++) { var startIndex = rowPointers[row]; var endIndex = rowPointers[row + 1]; if (startIndex == endIndex) { continue; } for (var column = 0; column < other.ColumnCount; column++) { int otherColumnStartPosition = column * other.RowCount; var sum = 0d; for (var index = startIndex; index < endIndex; index++) { sum += values[index] * denseOther.Data[otherColumnStartPosition + columnIndices[index]]; } result.At(row, column, sum); } } return; } var columnVector = new DenseVector(other.RowCount); for (var row = 0; row < RowCount; row++) { var startIndex = rowPointers[row]; var endIndex = rowPointers[row + 1]; 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); } } } void DoMultiplySparse(SparseMatrix other, SparseMatrix result) { result.Clear(); var ax = _storage.Values; var ap = _storage.RowPointers; var ai = _storage.ColumnIndices; var bx = other._storage.Values; var bp = other._storage.RowPointers; var bi = other._storage.ColumnIndices; int rows = RowCount; int cols = other.ColumnCount; int[] cp = result._storage.RowPointers; var marker = new int[cols]; for (int ib = 0; ib < cols; ib++) { marker[ib] = -1; } int count = 0; for (int i = 0; i < rows; i++) { // For each row of A for (int j = ap[i]; j < ap[i + 1]; j++) { // Row number to be added int a = ai[j]; for (int k = bp[a]; k < bp[a + 1]; k++) { int b = bi[k]; if (marker[b] != i) { marker[b] = i; count++; } } } // Record non-zero count. cp[i + 1] = count; } var ci = new int[count]; var cx = new double[count]; for (int ib = 0; ib < cols; ib++) { marker[ib] = -1; } count = 0; for (int i = 0; i < rows; i++) { int rowStart = cp[i]; for (int j = ap[i]; j < ap[i + 1]; j++) { int a = ai[j]; double aEntry = ax[j]; for (int k = bp[a]; k < bp[a + 1]; k++) { int b = bi[k]; double bEntry = bx[k]; if (marker[b] < rowStart) { marker[b] = count; ci[marker[b]] = b; cx[marker[b]] = aEntry * bEntry; count++; } else { cx[marker[b]] += aEntry * bEntry; } } } } result._storage.Values = cx; result._storage.ColumnIndices = ci; result._storage.Normalize(); } /// /// 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; for (var row = 0; row < RowCount; row++) { var startIndex = rowPointers[row]; var endIndex = rowPointers[row + 1]; 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) { if (other is SparseMatrix otherSparse && result is SparseMatrix resultSparse) { resultSparse.Clear(); var rowPointers = _storage.RowPointers; var values = _storage.Values; var otherStorage = otherSparse._storage; for (var j = 0; j < RowCount; j++) { var startIndexOther = otherStorage.RowPointers[j]; var endIndexOther = otherStorage.RowPointers[j + 1]; if (startIndexOther == endIndexOther) { continue; } for (var i = 0; i < RowCount; i++) { var startIndexThis = rowPointers[i]; var endIndexThis = rowPointers[i + 1]; 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._storage.At(i, j, sum + result.At(i, j)); } } } else { base.DoTransposeAndMultiply(other, result); } } /// /// Multiplies the transpose of 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 DoTransposeThisAndMultiply(Vector rightSide, Vector result) { var rowPointers = _storage.RowPointers; var columnIndices = _storage.ColumnIndices; var values = _storage.Values; for (var row = 0; row < RowCount; row++) { var startIndex = rowPointers[row]; var endIndex = rowPointers[row + 1]; if (startIndex == endIndex) { continue; } var rightSideValue = rightSide[row]; for (var index = startIndex; index < endIndex; index++) { result[columnIndices[index]] += values[index] * rightSideValue; } } } /// /// 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; for (var i = 0; i < RowCount; i++) { var endIndex = rowPointers[i + 1]; for (var j = rowPointers[i]; 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 divisor, Matrix result) { result.Clear(); var rowPointers = _storage.RowPointers; var columnIndices = _storage.ColumnIndices; var values = _storage.Values; for (var i = 0; i < RowCount; i++) { var endIndex = rowPointers[i + 1]; for (var j = rowPointers[i]; j < endIndex; j++) { if (values[j] != 0d) { result.At(i, columnIndices[j], values[j]/divisor.At(i, columnIndices[j])); } } } } public override void KroneckerProduct(Matrix other, Matrix result) { if (other == null) { throw new ArgumentNullException(nameof(other)); } if (result == null) { throw new ArgumentNullException(nameof(result)); } if (result.RowCount != (RowCount*other.RowCount) || result.ColumnCount != (ColumnCount*other.ColumnCount)) { throw DimensionsDontMatch(this, other, result); } var rowPointers = _storage.RowPointers; var columnIndices = _storage.ColumnIndices; var values = _storage.Values; for (var i = 0; i < RowCount; i++) { var endIndex = rowPointers[i + 1]; for (var j = rowPointers[i]; j < endIndex; j++) { if (values[j] != 0d) { result.SetSubMatrix(i*other.RowCount, other.RowCount, columnIndices[j]*other.ColumnCount, other.ColumnCount, values[j]*other); } } } } /// /// Computes the canonical modulus, where the result has the sign of the divisor, /// for the given divisor each element of the matrix. /// /// The scalar denominator to use. /// Matrix to store the results in. protected override void DoModulus(double divisor, Matrix result) { if (result is SparseMatrix sparseResult) { if (!ReferenceEquals(this, result)) { CopyTo(result); } var resultStorage = sparseResult._storage; for (var index = 0; index < resultStorage.Values.Length; index++) { resultStorage.Values[index] = Euclid.Modulus(resultStorage.Values[index], divisor); } } else { base.DoModulus(divisor, result); } } /// /// Computes the remainder (% operator), where the result has the sign of the dividend, /// for the given divisor each element of the matrix. /// /// The scalar denominator to use. /// Matrix to store the results in. protected override void DoRemainder(double divisor, Matrix result) { if (result is SparseMatrix sparseResult) { if (!ReferenceEquals(this, result)) { CopyTo(result); } var resultStorage = sparseResult._storage; for (var index = 0; index < resultStorage.Values.Length; index++) { resultStorage.Values[index] %= divisor; } } else { base.DoRemainder(divisor, result); } } /// /// Evaluates whether this matrix is symmetric. /// public override bool IsSymmetric() { if (RowCount != ColumnCount) { return false; } 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]; if (!values[index].Equals(At(column, row))) { 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(nameof(rightSide)); } if (leftSide == null) { throw new ArgumentNullException(nameof(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(nameof(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(nameof(rightSide)); } if (leftSide == null) { throw new ArgumentNullException(nameof(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(nameof(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(nameof(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(nameof(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(nameof(leftSide)); } if (rightSide == null) { throw new ArgumentNullException(nameof(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(nameof(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(nameof(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(nameof(leftSide)); } return (SparseMatrix)leftSide.Remainder(rightSide); } public override string ToTypeString() { return FormattableString.Invariant($"SparseMatrix {RowCount}x{ColumnCount}-Double {NonZerosCount / (RowCount * (double) ColumnCount):P2} Filled"); } } }