// // 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 Threading; /// /// A Matrix class with sparse storage. The underlying storage scheme is 3-array compressed-sparse-row (CSR) Format. /// Wikipedia - CSR. /// public class SparseMatrix : Matrix { /// /// The array containing the row indices of the existing rows. Element "j" of the array gives the index of the /// element in the array that is first non-zero element in a row "j" /// private readonly int[] _rowIndex = new int[0]; /// /// Array that contains the non-zero elements of matrix. Values of the non-zero elements of matrix are mapped into the values /// array using the row-major storage mapping described in a compressed sparse row (CSR) format. /// private Complex[] _nonZeroValues = new Complex[0]; /// /// Gets the number of non zero elements in the matrix. /// /// The number of non zero elements. public int NonZerosCount { get; private set; } /// /// An array containing the column indices of the non-zero values. Element "I" of the array /// is the number of the column in matrix that contains the I-th value in the array. /// private int[] _columnIndices = new int[0]; /// /// Initializes a new instance of the class. /// /// /// The number of rows. /// /// /// The number of columns. /// public SparseMatrix(int rows, int columns) : base(rows, columns) { _rowIndex = new int[rows]; } /// /// 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, Complex value) : this(rows, columns) { if (value == 0.0) { return; } NonZerosCount = rows * columns; _nonZeroValues = new Complex[NonZerosCount]; _columnIndices = new int[NonZerosCount]; for (int i = 0, j = 0; i < _nonZeroValues.Length; i++, j++) { // Reset column position to "0" if (j == columns) { j = 0; } _nonZeroValues[i] = value; _columnIndices[i] = j; } // Set proper row pointers for (var i = 0; i < _rowIndex.Length; i++) { _rowIndex[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++) { SetValueAt(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)) { var rows = array.GetLength(0); var columns = array.GetLength(1); for (var i = 0; i < rows; i++) { for (var j = 0; j < columns; j++) { SetValueAt(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++) { SetValueAt(i, j, matrix.At(i, j)); } } } else { NonZerosCount = sparseMatrix.NonZerosCount; _rowIndex = new int[rows]; _columnIndices = new int[NonZerosCount]; _nonZeroValues = new Complex[NonZerosCount]; Array.Copy(sparseMatrix._nonZeroValues, _nonZeroValues, NonZerosCount); Array.Copy(sparseMatrix._columnIndices, _columnIndices, NonZerosCount); Array.Copy(sparseMatrix._rowIndex, _rowIndex, rows); } } /// /// 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 new ArgumentException(Resources.ArgumentMatrixDimensions, "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) { for (var row = 0; row < result.RowCount; row++) { var startIndex = _rowIndex[row]; var endIndex = row < _rowIndex.Length - 1 ? _rowIndex[row + 1] : NonZerosCount; for (var j = startIndex; j < endIndex; j++) { if (row >= _columnIndices[j]) { result.At(row, _columnIndices[j], _nonZeroValues[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 new ArgumentException(Resources.ArgumentMatrixDimensions, "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) { for (var row = 0; row < result.RowCount; row++) { var startIndex = _rowIndex[row]; var endIndex = row < _rowIndex.Length - 1 ? _rowIndex[row + 1] : NonZerosCount; for (var j = startIndex; j < endIndex; j++) { if (row <= _columnIndices[j]) { result.At(row, _columnIndices[j], _nonZeroValues[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 rowLength, int columnIndex, int columnLength) { if (rowIndex >= RowCount || rowIndex < 0) { throw new ArgumentOutOfRangeException("rowIndex"); } if (columnIndex >= ColumnCount || columnIndex < 0) { throw new ArgumentOutOfRangeException("columnIndex"); } if (rowLength < 1) { throw new ArgumentException(Resources.ArgumentMustBePositive, "rowLength"); } if (columnLength < 1) { throw new ArgumentException(Resources.ArgumentMustBePositive, "columnLength"); } var colMax = columnIndex + columnLength; var rowMax = rowIndex + rowLength; if (rowMax > RowCount) { throw new ArgumentOutOfRangeException("rowLength"); } if (colMax > ColumnCount) { throw new ArgumentOutOfRangeException("columnLength"); } var result = (SparseMatrix)CreateMatrix(rowLength, columnLength); for (int i = rowIndex, row = 0; i < rowMax; i++, row++) { var startIndex = _rowIndex[i]; var endIndex = row < _rowIndex.Length - 1 ? _rowIndex[i + 1] : NonZerosCount; for (int j = startIndex; j < endIndex; j++) { // check if the column index is in the range if ((_columnIndices[j] >= columnIndex) && (_columnIndices[j] < columnIndex + columnLength)) { var column = _columnIndices[j] - columnIndex; result.SetValueAt(row, column, _nonZeroValues[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 new ArgumentException(Resources.ArgumentMatrixDimensions, "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) { for (var row = 0; row < result.RowCount; row++) { var startIndex = _rowIndex[row]; var endIndex = row < _rowIndex.Length - 1 ? _rowIndex[row + 1] : NonZerosCount; for (var j = startIndex; j < endIndex; j++) { if (row > _columnIndices[j]) { result.At(row, _columnIndices[j], _nonZeroValues[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 new ArgumentException(Resources.ArgumentMatrixDimensions, "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) { for (var row = 0; row < result.RowCount; row++) { var startIndex = _rowIndex[row]; var endIndex = row < _rowIndex.Length - 1 ? _rowIndex[row + 1] : NonZerosCount; for (var j = startIndex; j < endIndex; j++) { if (row < _columnIndices[j]) { result.At(row, _columnIndices[j], _nonZeroValues[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 Complex[] ToColumnWiseArray() { var ret = new Complex[RowCount * ColumnCount]; for (var j = 0; j < ColumnCount; j++) { for (var i = 0; i < RowCount; i++) { var index = FindItem(i, j); ret[(j * RowCount) + i] = index >= 0 ? _nonZeroValues[index] : 0.0; } } return ret; } /// /// Retrieves the requested element without range checking. /// /// /// The row of the element. /// /// /// The column of the element. /// /// /// The requested element. /// public override Complex At(int row, int column) { var index = FindItem(row, column); return index >= 0 ? _nonZeroValues[index] : 0.0; } /// /// Sets the value of the given element. /// /// /// The row of the element. /// /// /// The column of the element. /// /// /// The value to set the element to. /// public override void At(int row, int column, Complex value) { SetValueAt(row, column, value); } #region Internal methods - CRS storage implementation /// /// Created this method because we cannot call "virtual At" in constructor of the class, but we need to do it /// /// The row of the element. /// The column of the element. /// The value to set the element to. /// WARNING: This method is not thread safe. Use "lock" with it and be sure to avoid deadlocks private void SetValueAt(int row, int column, Complex value) { var index = FindItem(row, column); if (index >= 0) { // Non-zero item found in matrix if (value == 0.0) { // Delete existing item DeleteItemByIndex(index, row); } else { // Update item _nonZeroValues[index] = value; } } else { // Item not found. Add new value if (value == 0.0) { return; } index = ~index; // Check if the storage needs to be increased if ((NonZerosCount == _nonZeroValues.Length) && (NonZerosCount < (RowCount * ColumnCount))) { // Value array is completely full so we increase the size // Determine the increase in size. We will not grow beyond the size of the matrix var size = Math.Min(_nonZeroValues.Length + GrowthSize(), RowCount * ColumnCount); Array.Resize(ref _nonZeroValues, size); Array.Resize(ref _columnIndices, size); } // Move all values (with an position larger than index) in the value array to the next position // move all values (with an position larger than index) in the columIndices array to the next position for (var i = NonZerosCount - 1; i > index - 1; i--) { _nonZeroValues[i + 1] = _nonZeroValues[i]; _columnIndices[i + 1] = _columnIndices[i]; } // Add the value and the column index _nonZeroValues[index] = value; _columnIndices[index] = column; // increase the number of non-zero numbers by one NonZerosCount += 1; // add 1 to all the row indices for rows bigger than rowIndex // so that they point to the correct part of the value array again. for (var i = row + 1; i < _rowIndex.Length; i++) { _rowIndex[i] += 1; } } } /// /// Delete value from internal storage /// /// Index of value in nonZeroValues array /// Row number of matrix /// WARNING: This method is not thread safe. Use "lock" with it and be sure to avoid deadlocks private void DeleteItemByIndex(int itemIndex, int row) { // Move all values (with an position larger than index) in the value array to the previous position // move all values (with an position larger than index) in the columIndices array to the previous position for (var i = itemIndex + 1; i < NonZerosCount; i++) { _nonZeroValues[i - 1] = _nonZeroValues[i]; _columnIndices[i - 1] = _columnIndices[i]; } // Decrease value in Row for (var i = row + 1; i < _rowIndex.Length; i++) { _rowIndex[i] -= 1; } NonZerosCount -= 1; // Check if the storage needs to be shrink. This is reasonable to do if // there are a lot of non-zero elements and storage is two times bigger if ((NonZerosCount > 1024) && (NonZerosCount < _nonZeroValues.Length / 2)) { Array.Resize(ref _nonZeroValues, NonZerosCount); Array.Resize(ref _columnIndices, NonZerosCount); } } /// /// Find item Index in nonZeroValues array /// /// Matrix row index /// Matrix column index /// Item index /// WARNING: This method is not thread safe. Use "lock" with it and be sure to avoid deadlocks private int FindItem(int row, int column) { // Determin bounds in columnIndices array where this item should be searched (using rowIndex) var startIndex = _rowIndex[row]; var endIndex = row < _rowIndex.Length - 1 ? _rowIndex[row + 1] : NonZerosCount; return Array.BinarySearch(_columnIndices, startIndex, endIndex - startIndex, column); } /// /// Calculates the amount with which to grow the storage array's if they need to be /// increased in size. /// /// The amount grown. private int GrowthSize() { int delta; if (_nonZeroValues.Length > 1024) { delta = _nonZeroValues.Length / 4; } else { if (_nonZeroValues.Length > 256) { delta = 512; } else { delta = _nonZeroValues.Length > 64 ? 128 : 32; } } return delta; } #endregion /// /// Sets all values to zero. /// public override void Clear() { NonZerosCount = 0; Array.Clear(_rowIndex, 0, _rowIndex.Length); } /// /// Copies the elements of this matrix to the given matrix. /// /// /// The matrix to copy values into. /// /// /// If target is . /// /// /// If this and the target matrix do not have the same dimensions.. /// public override void CopyTo(Matrix target) { var sparseTarget = target as SparseMatrix; if (sparseTarget == null) { base.CopyTo(target); } else { if (ReferenceEquals(this, target)) { return; } if (RowCount != target.RowCount || ColumnCount != target.ColumnCount) { throw new ArgumentException(Resources.ArgumentMatrixDimensions, "target"); } // Lets copy only needed data. Portion of needed data is determined by NonZerosCount value sparseTarget._nonZeroValues = new Complex[NonZerosCount]; sparseTarget._columnIndices = new int[NonZerosCount]; sparseTarget.NonZerosCount = NonZerosCount; Array.Copy(_nonZeroValues, sparseTarget._nonZeroValues, NonZerosCount); Array.Copy(_columnIndices, sparseTarget._columnIndices, NonZerosCount); Array.Copy(_rowIndex, sparseTarget._rowIndex, RowCount); } } /// /// Returns a hash code for this instance. /// /// /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. /// public override int GetHashCode() { var hashNum = Math.Min(NonZerosCount, 25); long hash = 0; for (var i = 0; i < hashNum; i++) { #if SILVERLIGHT hash ^= Precision.DoubleToInt64Bits(_nonZeroValues[i].Magnitude); #else hash ^= BitConverter.DoubleToInt64Bits(_nonZeroValues[i].Magnitude); #endif } return BitConverter.ToInt32(BitConverter.GetBytes(hash), 4); } /// /// Returns the transpose of this matrix. /// /// The transpose of this matrix. public override Matrix Transpose() { var ret = new SparseMatrix(ColumnCount, RowCount) { _columnIndices = new int[NonZerosCount], _nonZeroValues = new Complex[NonZerosCount] }; // Do an 'inverse' CopyTo iterate over the rows for (var i = 0; i < _rowIndex.Length; i++) { // Get the begin / end index for the current row var startIndex = _rowIndex[i]; var endIndex = i < _rowIndex.Length - 1 ? _rowIndex[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.SetValueAt(_columnIndices[j], i, _nonZeroValues[j]); } } return ret; } /// Calculates the Frobenius norm of this matrix. /// The Frobenius norm of this matrix. public override Complex FrobeniusNorm() { var transpose = (SparseMatrix)Transpose(); var aat = (SparseMatrix)(this * transpose); var norm = 0.0; for (var i = 0; i < aat._rowIndex.Length; i++) { // Get the begin / end index for the current row var startIndex = aat._rowIndex[i]; var endIndex = i < aat._rowIndex.Length - 1 ? aat._rowIndex[i + 1] : aat.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++) { if (i == aat._columnIndices[j]) { norm += aat._nonZeroValues[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 norm = 0.0; for (var i = 0; i < _rowIndex.Length; i++) { // Get the begin / end index for the current row var startIndex = _rowIndex[i]; var endIndex = i < _rowIndex.Length - 1 ? _rowIndex[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; } var s = 0.0; for (var j = startIndex; j < endIndex; j++) { s += _nonZeroValues[j].Magnitude; } 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 ArgumentException(Resources.ArgumentMustBePositive, "length"); } if (result.Count < length) { throw new ArgumentException(Resources.ArgumentVectorsSameLength, "result"); } // Determine bounds in columnIndices array where this item should be searched (using rowIndex) var startIndex = _rowIndex[rowIndex]; var endIndex = rowIndex < _rowIndex.Length - 1 ? _rowIndex[rowIndex + 1] : NonZerosCount; 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 = FindItem(rowIndex, i); result[j] = index >= 0 ? _nonZeroValues[index] : 0.0; } } } /// /// 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 { if (resultSparseMatrix.RowCount != RowCount + lowerSparseMatrix.RowCount || resultSparseMatrix.ColumnCount != ColumnCount + lowerSparseMatrix.ColumnCount) { throw new ArgumentException(Resources.ArgumentMatrixDimensions, "result"); } resultSparseMatrix.NonZerosCount = NonZerosCount + lowerSparseMatrix.NonZerosCount; resultSparseMatrix._nonZeroValues = new Complex[resultSparseMatrix.NonZerosCount]; resultSparseMatrix._columnIndices = new int[resultSparseMatrix.NonZerosCount]; Array.Copy(_nonZeroValues, 0, resultSparseMatrix._nonZeroValues, 0, NonZerosCount); Array.Copy(lowerSparseMatrix._nonZeroValues, 0, resultSparseMatrix._nonZeroValues, NonZerosCount, lowerSparseMatrix.NonZerosCount); Array.Copy(_columnIndices, 0, resultSparseMatrix._columnIndices, 0, NonZerosCount); Array.Copy(_rowIndex, 0, resultSparseMatrix._rowIndex, 0, RowCount); // Copy and adjust lower column indices and rowIndex for (int i = NonZerosCount, j = 0; i < resultSparseMatrix.NonZerosCount; i++, j++) { resultSparseMatrix._columnIndices[i] = lowerSparseMatrix._columnIndices[j] + ColumnCount; } for (int i = RowCount, j = 0; i < resultSparseMatrix.RowCount; i++, j++) { resultSparseMatrix._rowIndex[i] = lowerSparseMatrix._rowIndex[j] + NonZerosCount; } } } #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) { NonZerosCount = order, _nonZeroValues = new Complex[order], _columnIndices = new int[order] }; for (var i = 0; i < order; i++) { m._nonZeroValues[i] = 1.0; m._columnIndices[i] = i; m._rowIndex[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); } if (NonZerosCount != sparseMatrix.NonZerosCount) { return false; } // If all else fails, perform element wise comparison. for (var index = 0; index < NonZerosCount; index++) { if (!_nonZeroValues[index].AlmostEqual(sparseMatrix._nonZeroValues[index]) || _columnIndices[index] != sparseMatrix._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) { result.Clear(); var sparseResult = result as SparseMatrix; if (sparseResult == null) { for (var i = 0; i < other.RowCount; i++) { // Get the begin / end index for the current row var startIndex = _rowIndex[i]; var endIndex = i < _rowIndex.Length - 1 ? _rowIndex[i + 1] : NonZerosCount; for (var j = startIndex; j < endIndex; j++) { var resVal = _nonZeroValues[j] + other.At(i, _columnIndices[j]); if (resVal != 0.0) { result.At(i, _columnIndices[j], resVal); } } } } else { for (var i = 0; i < other.RowCount; i++) { // Get the begin / end index for the current row var startIndex = _rowIndex[i]; var endIndex = i < _rowIndex.Length - 1 ? _rowIndex[i + 1] : NonZerosCount; for (var j = startIndex; j < endIndex; j++) { var resVal = _nonZeroValues[j] + other.At(i, _columnIndices[j]); if (resVal != 0.0) { sparseResult.SetValueAt(i, _columnIndices[j], 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) { result.Clear(); var sparseResult = result as SparseMatrix; if (sparseResult == null) { for (var i = 0; i < other.RowCount; i++) { // Get the begin / end index for the current row var startIndex = _rowIndex[i]; var endIndex = i < _rowIndex.Length - 1 ? _rowIndex[i + 1] : NonZerosCount; for (var j = startIndex; j < endIndex; j++) { var resVal = _nonZeroValues[j] - other.At(i, _columnIndices[j]); if (resVal != 0.0) { result.At(i, _columnIndices[j], resVal); } } } } else { for (var i = 0; i < other.RowCount; i++) { // Get the begin / end index for the current row var startIndex = _rowIndex[i]; var endIndex = i < _rowIndex.Length - 1 ? _rowIndex[i + 1] : NonZerosCount; for (var j = startIndex; j < endIndex; j++) { var resVal = _nonZeroValues[j] - other.At(i, _columnIndices[j]); if (resVal != 0.0) { sparseResult.SetValueAt(i, _columnIndices[j], 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(); for (var row = 0; row < RowCount; row++) { var start = _rowIndex[row]; var end = _rowIndex[row + 1]; if (start == end) { continue; } for (var index = start; index < end; index++) { var column = _columnIndices[index]; result.At(row, column, _nonZeroValues[index] * scalar); } } } else { if (!ReferenceEquals(this, result)) { CopyTo(sparseResult); } CommonParallel.For(0, NonZerosCount, index => sparseResult._nonZeroValues[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 columnVector = new DenseVector(other.RowCount); for (var row = 0; row < RowCount; row++) { // Get the begin / end index for the current row var startIndex = _rowIndex[row]; var endIndex = row < _rowIndex.Length - 1 ? _rowIndex[row + 1] : NonZerosCount; 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 += _nonZeroValues[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) { for (var row = 0; row < RowCount; row++) { // Get the begin / end index for the current row var startIndex = _rowIndex[row]; var endIndex = row < _rowIndex.Length - 1 ? _rowIndex[row + 1] : NonZerosCount; if (startIndex == endIndex) { continue; } var sum = Complex.Zero; for (var index = startIndex; index < endIndex; index++) { sum += _nonZeroValues[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(); for (var j = 0; j < RowCount; j++) { // Get the begin / end index for the row var startIndexOther = otherSparse._rowIndex[j]; var endIndexOther = j < otherSparse._rowIndex.Length - 1 ? otherSparse._rowIndex[j + 1] : otherSparse.NonZerosCount; 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 = _rowIndex[i]; var endIndexThis = i < _rowIndex.Length - 1 ? _rowIndex[i + 1] : NonZerosCount; if (startIndexThis == endIndexThis) { continue; } var sum = Complex.Zero; for (var index = startIndexOther; index < endIndexOther; index++) { var ind = FindItem(i, otherSparse._columnIndices[index]); if (ind >= 0) { sum += otherSparse._nonZeroValues[index] * _nonZeroValues[ind]; } } resultSparse.SetValueAt(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(); for (var i = 0; i < other.RowCount; i++) { // Get the begin / end index for the current row var startIndex = _rowIndex[i]; var endIndex = i < _rowIndex.Length - 1 ? _rowIndex[i + 1] : NonZerosCount; for (var j = startIndex; j < endIndex; j++) { var resVal = _nonZeroValues[j] * other.At(i, _columnIndices[j]); if (resVal != 0.0) { 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(); for (var i = 0; i < other.RowCount; i++) { // Get the begin / end index for the current row var startIndex = _rowIndex[i]; var endIndex = i < _rowIndex.Length - 1 ? _rowIndex[i + 1] : NonZerosCount; for (var j = startIndex; j < endIndex; j++) { var resVal = _nonZeroValues[j] / other.At(i, _columnIndices[j]); if (resVal != 0.0) { result.At(i, _columnIndices[j], resVal); } } } } /// /// 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() { for (var row = 0; row < RowCount - 1; row++) { var start = _rowIndex[row]; var end = _rowIndex[row + 1]; if (start == end) { continue; } for (var index = start; index < end; index++) { yield return new Tuple(row, _columnIndices[index], _nonZeroValues[index]); } } var lastRow = _rowIndex.Length - 1; if (_rowIndex[lastRow] < NonZerosCount) { for (var index = _rowIndex[lastRow]; index < NonZerosCount; index++) { yield return new Tuple(lastRow, _columnIndices[index], _nonZeroValues[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 for (var row = 0; row < RowCount - 1; row++) { var start = _rowIndex[row]; var end = _rowIndex[row + 1]; if (start == end) { continue; } if (!CheckIfOppositesAreEqual(start, end, row)) { return false; } } var lastRow = _rowIndex.Length - 1; if (_rowIndex[lastRow] < NonZerosCount) { if (!CheckIfOppositesAreEqual(_rowIndex[lastRow], NonZerosCount, 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) { for (var index = start; index < end; index++) { var column = _columnIndices[index]; var opposite = At(column, row); if (!_nonZeroValues[index].Equals(opposite)) { return false; } } return true; } } }