// // 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.Complex { using System; using System.Collections.Generic; using System.Numerics; 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; } } /// /// Initializes a new instance of the class. /// public 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, Complex.Zero)) { } /// /// 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. [Obsolete("Use a dense matrix instead.")] public SparseMatrix(int rows, int columns, Complex value) : this(rows, columns) { if (value.IsZero()) { return; } var rowPointers = _storage.RowPointers; var valueCount = _storage.ValueCount = rows * columns; var columnIndices = _storage.ColumnIndices = new int[valueCount]; var values = _storage.Values = new Complex[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, Complex[] 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(Complex[,] 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) { matrix.Storage.CopyToUnchecked(Storage, skipClearing: true); } /// /// Creates a SparseMatrix for the given number of rows and columns. /// /// The number of rows. /// The number of columns. /// True if all fields must be mutable (e.g. not a diagonal matrix). /// /// A SparseMatrix with the given dimensions. /// public override Matrix CreateMatrix(int numberOfRows, int numberOfColumns, bool fullyMutable = false) { return new SparseMatrix(numberOfRows, numberOfColumns); } /// /// Creates a with a the given dimension. /// /// The size of the vector. /// True if all fields must be mutable. /// /// A with the given dimension. /// public override Vector CreateVector(int size, bool fullyMutable = false) { 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); } 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); } 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]); } } } } /// /// 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); } 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); } 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 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 SparseCompressedRowMatrixStorage(ColumnCount, RowCount, Complex.Zero) { ColumnIndices = new int[valueCount], Values = new Complex[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] : NonZerosCount; // 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++) { ret.At(columnIndices[j], i, values[j]); } } return new SparseMatrix(ret); } /// Calculates the Frobenius norm of this matrix. /// The Frobenius norm of this matrix. public override Complex FrobeniusNorm() { var aat = (SparseCompressedRowMatrixStorage) (this*ConjugateTranspose()).Storage; 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 += aat.Values[j].Magnitude; } } } norm = Math.Sqrt(norm); return norm; } /// Calculates the infinity norm of this matrix. /// The infinity norm of this matrix. public override Complex 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 = 0.0; for (var j = startIndex; j < endIndex; j++) { s += values[j].Magnitude; } norm = Math.Max(norm, s); } return norm; } #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 SparseCompressedRowMatrixStorage(order, order, Complex.Zero) { ValueCount = order, Values = new Complex[order], ColumnIndices = new int[order] }; for (var i = 0; i < order; i++) { m.Values[i] = 1d; m.ColumnIndices[i] = i; m.RowPointers[i] = i; } return new SparseMatrix(m); } #endregion /// /// 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._storage; 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._storage; 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(Complex scalar, Matrix result) { if (scalar == 1.0) { CopyTo(result); return; } if (scalar == 0.0 || NonZerosCount == 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, NonZerosCount, index => sparseResult._storage.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 = Complex.Zero; 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 = Complex.Zero; 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._storage; 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 = Complex.Zero; 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)); } } } /// /// 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 < 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.IsZero()) { 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 < 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++) { if (!values[j].IsZero()) { result.At(i, columnIndices[j], values[j]/other.At(i, columnIndices[j])); } } } } public override void KroneckerProduct(Matrix other, Matrix result) { if (other == null) { throw new ArgumentNullException("other"); } if (result == null) { throw new ArgumentNullException("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; 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++) { if (!values[j].IsZero()) { result.SetSubMatrix(i*other.RowCount, other.RowCount, columnIndices[j]*other.ColumnCount, other.ColumnCount, values[j]*other); } } } } /// /// 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] < NonZerosCount) { 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, Complex 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 *(Complex 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, Complex rightSide) { if (leftSide == null) { throw new ArgumentNullException("leftSide"); } return (SparseMatrix)leftSide.Modulus(rightSide); } } }