From 91245be3a42faaa6a65c60631482794c96d73e85 Mon Sep 17 00:00:00 2001 From: Jong Hyun Kim Date: Tue, 11 Aug 2020 10:01:48 +0900 Subject: [PATCH] Add more comments in building sparse matrix from COO, CSR, and CSC formats. Explicit zeros are not intentionally removed. --- src/Numerics/LinearAlgebra/Builder.cs | 173 +++++++++++------- .../SparseCompressedRowMatrixStorage.cs | 5 +- 2 files changed, 105 insertions(+), 73 deletions(-) diff --git a/src/Numerics/LinearAlgebra/Builder.cs b/src/Numerics/LinearAlgebra/Builder.cs index 4b550830..0a43c00b 100644 --- a/src/Numerics/LinearAlgebra/Builder.cs +++ b/src/Numerics/LinearAlgebra/Builder.cs @@ -1166,6 +1166,7 @@ namespace MathNet.Numerics.LinearAlgebra return m; } + // Representation of Sparse Matrix // // Matrix A = [ 0 b 0 h 0 0 ] @@ -1173,22 +1174,22 @@ namespace MathNet.Numerics.LinearAlgebra // [ 0 0 f j l n ] // [ 0 d g k m 0 ] // - // Rows = 4, Columns = 6, NonZeroCount = 14 + // rows = 4, columns = 6, valueCount = 14 // - // (1) COO, Coordinate Format: + // (1) COO, Coordinate, ijv, or triplet format: // cooRowIndices = { 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3 } // cooColumnIndices = { 1, 3, 0, 1, 2, 3, 2, 3, 4, 5, 1, 2, 3, 4 } // cooValues = { b, h, a, c, e, i, f, j, l, n, d, g, k, m } // - // (2) CSR, Compressed Sparse Row representation: + // (2) CSR, Compressed Sparse Row or Compressed Row Storage(CRS) or Yale format: // csrRowPointers = { 0, 2, 6, 10, 14 } // csrColumnIndices = { 1, 3, 0, 1, 2, 3, 2, 3, 4, 5, 1, 2, 3, 4 } // csrValues = { b, h, a, c, e, i, f, j, l, n, d, g, k, m } // - // (3) CSC, Compressed Sparse Column representation: - // csrColumnPointers = { 0, 1, 4, 7, 11, 13, 14 } - // csrRowIndices = { 1, 0, 1, 3, 1, 2, 3, 0, 1, 2, 3, 2, 3, 2 } - // csrValues = { a, b, c, d, e, f, g, h, i, j, k, l, m, n } + // (3) CSC, Compressed Sparse Column or Compressed Column Storage(CCS) format: + // cscColumnPointers = { 0, 1, 4, 7, 11, 13, 14 } + // cscRowIndices = { 1, 0, 1, 3, 1, 2, 3, 0, 1, 2, 3, 2, 3, 2 } + // cscValues = { a, b, c, d, e, f, g, h, i, j, k, l, m, n } /// @@ -1196,29 +1197,38 @@ namespace MathNet.Numerics.LinearAlgebra /// This new matrix will be independent from the given arrays. /// A new memory block will be allocated for storing the matrix. /// - public Matrix SparseFromCoordinateFormat(int rows, int columns, int nonZeroCount, int[] cooRowIndices, int[] cooColumnIndices, T[] cooValues) - { - if (cooValues == null) - throw new NullReferenceException(nameof(cooValues)); - if (cooRowIndices == null) - throw new NullReferenceException(nameof(cooRowIndices)); - if (cooColumnIndices == null) - throw new NullReferenceException(nameof(cooColumnIndices)); - - if (cooRowIndices.Length < nonZeroCount || cooColumnIndices.Length < nonZeroCount || cooValues.Length < nonZeroCount) + /// The number of rows. + /// The number of columns. + /// The number of stored values including explicit zeros. + /// The row index array of the coordinate format. + /// The column index array of the coordinate format. + /// The data array of the coordinate format. + /// The sparse matrix from the coordinate format. + /// Duplicate entries will be summed together and + /// explicit zeros will be not intentionally removed. + public Matrix SparseFromCoordinateFormat(int rows, int columns, int valueCount, int[] rowIndices, int[] columnIndices, T[] values) + { + if (values == null) + throw new NullReferenceException(nameof(values)); + if (rowIndices == null) + throw new NullReferenceException(nameof(rowIndices)); + if (columnIndices == null) + throw new NullReferenceException(nameof(columnIndices)); + + if (rowIndices.Length < valueCount || columnIndices.Length < valueCount || values.Length < valueCount) { - throw new Exception($"The given array has the wrong length. Should be {nonZeroCount}."); + throw new Exception($"The given array has the wrong length. Should be {valueCount}."); } // convert from COO to CSR - var csrValues = new T[nonZeroCount]; - var csrColumnIndices = new int[nonZeroCount]; + var csrValues = new T[valueCount]; + var csrColumnIndices = new int[valueCount]; var csrRowPointers = new int[rows + 1]; - for (int i = 0; i < nonZeroCount; i++) + for (int i = 0; i < valueCount; i++) { - csrRowPointers[cooRowIndices[i]]++; + csrRowPointers[rowIndices[i]]++; } for (int i = 0, cumsum = 0; i < rows; i++) { @@ -1226,15 +1236,15 @@ namespace MathNet.Numerics.LinearAlgebra csrRowPointers[i] = cumsum; cumsum += temp; } - csrRowPointers[rows] = nonZeroCount; + csrRowPointers[rows] = valueCount; - for (int i = 0; i < nonZeroCount; i++) + for (int i = 0; i < valueCount; i++) { - var row = cooRowIndices[i]; + var row = rowIndices[i]; var loc = csrRowPointers[row]; - csrColumnIndices[loc] = cooColumnIndices[i]; - csrValues[loc] = cooValues[i]; + csrColumnIndices[loc] = columnIndices[i]; + csrValues[loc] = values[i]; csrRowPointers[row]++; } @@ -1254,31 +1264,42 @@ namespace MathNet.Numerics.LinearAlgebra /// This new matrix will be independent from the given arrays. /// A new memory block will be allocated for storing the matrix. /// - public Matrix SparseFromCompressedSparseRowFormat(int rows, int columns, int nonZeroCount, int[] csrRowPointers, int[] csrColumnIndices, T[] csrValues) - { - if (csrValues == null) - throw new NullReferenceException(nameof(csrValues)); - if (csrColumnIndices == null) - throw new NullReferenceException(nameof(csrColumnIndices)); - if (csrRowPointers == null) - throw new NullReferenceException(nameof(csrRowPointers)); - if (csrRowPointers.Length < rows) + /// The number of rows. + /// The number of columns. + /// The number of stored values including explicit zeros. + /// The row pointer array of the compressed sparse row format. + /// The column index array of the compressed sparse row format. + /// The data array of the compressed sparse row format. + /// The sparse matrix from the compressed sparse row format. + /// Duplicate entries will be summed together and + /// explicit zeros will be not intentionally removed. + public Matrix SparseFromCompressedSparseRowFormat(int rows, int columns, int valueCount, int[] rowPointers, int[] columnIndices, T[] values) + { + if (values == null) + throw new NullReferenceException(nameof(values)); + if (columnIndices == null) + throw new NullReferenceException(nameof(columnIndices)); + if (rowPointers == null) + throw new NullReferenceException(nameof(rowPointers)); + if (rowPointers.Length < rows) { throw new Exception($"The given array has the wrong length. Should be {rows + 1}."); } - if (nonZeroCount != csrRowPointers[rows]) + if (valueCount != rowPointers[rows]) { - throw new Exception($"{nameof(nonZeroCount)} should be same to {csrRowPointers[rows]}"); + throw new Exception($"{nameof(valueCount)} should be same to {rowPointers[rows]}"); } - var values = new T[nonZeroCount]; - Array.Copy(csrValues, values, nonZeroCount); - var columnIndices = new int[nonZeroCount]; - Array.Copy(csrColumnIndices, columnIndices, nonZeroCount); - var rowPointers = new int[rows + 1]; - Array.Copy(csrRowPointers, rowPointers, rows + 1); + // copy arrays to new memory block. + + var csrValues = new T[valueCount]; + Array.Copy(values, csrValues, valueCount); + var csrColumnIndices = new int[valueCount]; + Array.Copy(columnIndices, csrColumnIndices, valueCount); + var csrRowPointers = new int[rows + 1]; + Array.Copy(rowPointers, csrRowPointers, rows + 1); - var storage = new SparseCompressedRowMatrixStorage(rows, columns, rowPointers, columnIndices, values); + var storage = new SparseCompressedRowMatrixStorage(rows, columns, csrRowPointers, csrColumnIndices, csrValues); return Sparse(storage, true); } @@ -1287,34 +1308,43 @@ namespace MathNet.Numerics.LinearAlgebra /// This new matrix will be independent from the given arrays. /// A new memory block will be allocated for storing the matrix. /// - public Matrix SparseFromCompressedSparseColumnFormat(int rows, int columns, int nonZeroCount, int[] cscRowIndices, int[] cscColumnPointers, T[] cscValues) - { - if (cscValues == null) - throw new NullReferenceException(nameof(cscValues)); - if (cscRowIndices == null) - throw new NullReferenceException(nameof(cscRowIndices)); - if (cscColumnPointers == null) - throw new NullReferenceException(nameof(cscColumnPointers)); - if (cscColumnPointers.Length < columns) + /// The number of rows. + /// The number of columns. + /// The number of stored values including explicit zeros. + /// The row index array of the compressed sparse column format. + /// The column pointer array of the compressed sparse column format. + /// The data array of the compressed sparse column format. + /// The sparse matrix from the compressed sparse column format. + /// Duplicate entries will be summed together and + /// explicit zeros will be not intentionally removed. + public Matrix SparseFromCompressedSparseColumnFormat(int rows, int columns, int valueCount, int[] rowIndices, int[] columnPointers, T[] values) + { + if (values == null) + throw new NullReferenceException(nameof(values)); + if (rowIndices == null) + throw new NullReferenceException(nameof(rowIndices)); + if (columnPointers == null) + throw new NullReferenceException(nameof(columnPointers)); + if (columnPointers.Length < columns) { throw new Exception($"The given array has the wrong length. Should be {columns + 1}."); } - if (nonZeroCount != cscColumnPointers[columns]) + if (valueCount != columnPointers[columns]) { - throw new Exception($"{nameof(nonZeroCount)} should be same to {cscColumnPointers[columns]}"); + throw new Exception($"{nameof(valueCount)} should be same to {columnPointers[columns]}"); } // convert from CSC to CSR - var csrValues = new T[nonZeroCount]; + var csrValues = new T[valueCount]; var csrRowPointers = new int[rows + 1]; - var csrColumnIndices = new int[nonZeroCount]; + var csrColumnIndices = new int[valueCount]; for (int i = 0; i < columns; i++) { - for (int j = cscColumnPointers[i]; j < cscColumnPointers[i + 1]; j++) + for (int j = columnPointers[i]; j < columnPointers[i + 1]; j++) { - csrRowPointers[cscRowIndices[j] + 1]++; + csrRowPointers[rowIndices[j] + 1]++; } } for (int i = 1; i < rows + 1; i++) @@ -1324,12 +1354,12 @@ namespace MathNet.Numerics.LinearAlgebra var curr = new int[rows]; for (int i = 0; i < columns; i++) { - for (int j = cscColumnPointers[i]; j < cscColumnPointers[i + 1]; j++) + for (int j = columnPointers[i]; j < columnPointers[i + 1]; j++) { - var loc = csrRowPointers[cscRowIndices[j]] + curr[cscRowIndices[j]]; - curr[cscRowIndices[j]]++; + var loc = csrRowPointers[rowIndices[j]] + curr[rowIndices[j]]; + curr[rowIndices[j]]++; csrColumnIndices[loc] = i; - csrValues[loc] = cscValues[j]; + csrValues[loc] = values[j]; } } @@ -1337,9 +1367,10 @@ namespace MathNet.Numerics.LinearAlgebra return Sparse(storage, true); } + // Eliminate duplicate entries by adding them together. internal void SumDuplicates(SparseCompressedRowMatrixStorage storage) { - int nnz = 0; + int valueCount = 0; for (int i = 0; i < storage.RowCount; i++) { int index = storage.RowPointers[i]; @@ -1361,16 +1392,16 @@ namespace MathNet.Numerics.LinearAlgebra break; } } - storage.ColumnIndices[nnz] = col; - storage.Values[nnz] = val; - nnz++; + storage.ColumnIndices[valueCount] = col; + storage.Values[valueCount] = val; + valueCount++; } - storage.RowPointers[i + 1] = nnz; + storage.RowPointers[i + 1] = valueCount; } // Remove extra space from arrays. - Array.Resize(ref storage.Values, nnz); - Array.Resize(ref storage.ColumnIndices, nnz); + Array.Resize(ref storage.Values, valueCount); + Array.Resize(ref storage.ColumnIndices, valueCount); } internal abstract T AddEntries(T x, T y); diff --git a/src/Numerics/LinearAlgebra/Storage/SparseCompressedRowMatrixStorage.cs b/src/Numerics/LinearAlgebra/Storage/SparseCompressedRowMatrixStorage.cs index 7cd7ec57..41fe9fe8 100644 --- a/src/Numerics/LinearAlgebra/Storage/SparseCompressedRowMatrixStorage.cs +++ b/src/Numerics/LinearAlgebra/Storage/SparseCompressedRowMatrixStorage.cs @@ -85,8 +85,9 @@ namespace MathNet.Numerics.LinearAlgebra.Storage ColumnIndices = columnIndices; Values = values; - // columnIndices may be not ordered. - Normalize(); + // Explicit zeros are not intentionally removed. + // Sort ColumnIndices. + NormalizeOrdering(); } ///