Browse Source

Add methods to build a matrix from a COO, CSR, or CSC format.

v4
Jong Hyun Kim 7 years ago
parent
commit
abd3a6fdde
  1. 126
      src/Numerics.Tests/LinearAlgebraTests/MatrixStructureTheory.cs
  2. 169
      src/Numerics/LinearAlgebra/Builder.cs
  3. 8
      src/Numerics/LinearAlgebra/Storage/SparseCompressedRowMatrixStorage.cs

126
src/Numerics.Tests/LinearAlgebraTests/MatrixStructureTheory.cs

@ -30,6 +30,7 @@
using System;
using System.Linq;
using MathNet.Numerics.LinearAlgebra;
using MathNet.Numerics.LinearAlgebra.Storage;
using NUnit.Framework;
namespace MathNet.Numerics.UnitTests.LinearAlgebraTests
@ -580,6 +581,131 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests
Assert.That(matrix[i, j], Is.EqualTo(rows[i][j]));
}
[Test]
public void CanCreateSparseFromCoordinateFormat()
{
var rows = new[]
{
Vector<T>.Build.Random(4, 0),
Vector<T>.Build.Random(4, 1),
Vector<T>.Build.Random(4, 3)
};
var rowCount = rows.Length;
var columnCount = 4;
var valueCount = rowCount * columnCount;
var cooRowIndices = new int[valueCount];
var cooColumnIndices = new int[valueCount];
var cooValues = new T[valueCount];
int loc = 0;
for (int i = 0; i < rowCount; i++)
{
for (int j = 0; j < columnCount; j++)
{
cooRowIndices[loc] = i;
cooColumnIndices[loc] = j;
cooValues[loc] = rows[i].At(j);
loc++;
}
}
var matrix = Matrix<T>.Build.SparseFromCoordinateFormat(rowCount, columnCount, valueCount, cooRowIndices, cooColumnIndices, cooValues);
Assert.That(matrix.GetType().Name, Is.EqualTo("SparseMatrix"));
Assert.That(matrix.RowCount, Is.EqualTo(3));
Assert.That(matrix.ColumnCount, Is.EqualTo(4));
for (int j = 0; j < 4; j++)
for (int i = 0; i < 3; i++)
Assert.That(matrix[i, j], Is.EqualTo(rows[i][j]));
}
[Test]
public void CanCreateSparseFromCompressedSparseRowFormat()
{
var rows = new[]
{
Vector<T>.Build.Random(4, 0),
Vector<T>.Build.Random(4, 1),
Vector<T>.Build.Random(4, 3)
};
var rowCount = rows.Length;
var columnCount = 4;
var valueCount = rowCount * columnCount;
var csrRowPointers = new int[rowCount + 1];
var csrColumnIndices = new int[valueCount];
var csrValues = new T[valueCount];
int loc = 0;
for (int i = 0; i < rowCount; i++)
{
for (int j = 0; j < columnCount; j++)
{
csrRowPointers[i + 1]++;
csrColumnIndices[loc] = j;
csrValues[loc] = rows[i].At(j);
loc++;
}
}
for (int i = 1; i < rowCount + 1; i++)
{
csrRowPointers[i] += csrRowPointers[i - 1];
}
var matrix = Matrix<T>.Build.SparseFromCompressedSparseRowFormat(rowCount, columnCount, valueCount, csrRowPointers, csrColumnIndices, csrValues);
Assert.That(matrix.GetType().Name, Is.EqualTo("SparseMatrix"));
Assert.That(matrix.RowCount, Is.EqualTo(3));
Assert.That(matrix.ColumnCount, Is.EqualTo(4));
for (int j = 0; j < 4; j++)
for (int i = 0; i < 3; i++)
Assert.That(matrix[i, j], Is.EqualTo(rows[i][j]));
}
[Test]
public void CanCreateSparseFromCompressedSparseColumnFormat()
{
var rows = new[]
{
Vector<T>.Build.Random(4, 0),
Vector<T>.Build.Random(4, 1),
Vector<T>.Build.Random(4, 3)
};
var rowCount = rows.Length;
var columnCount = 4;
var valueCount = rowCount * columnCount;
var cscRowIndices = new int[valueCount];
var cscColumnPointers = new int[columnCount + 1];
var cscValues = new T[valueCount];
int loc = 0;
for (int j = 0; j < columnCount; j++)
{
for (int i = 0; i < rowCount; i++)
{
cscColumnPointers[j + 1]++;
cscRowIndices[loc] = i;
cscValues[loc] = rows[i].At(j);
loc++;
}
}
for (int i = 1; i < columnCount + 1; i++)
{
cscColumnPointers[i] += cscColumnPointers[i - 1];
}
var matrix = Matrix<T>.Build.SparseFromCompressedSparseColumnFormat(rowCount, columnCount, valueCount, cscRowIndices, cscColumnPointers, cscValues);
Assert.That(matrix.GetType().Name, Is.EqualTo("SparseMatrix"));
Assert.That(matrix.RowCount, Is.EqualTo(3));
Assert.That(matrix.ColumnCount, Is.EqualTo(4));
for (int j = 0; j < 4; j++)
for (int i = 0; i < 3; i++)
Assert.That(matrix[i, j], Is.EqualTo(rows[i][j]));
}
[Test]
public void CanEnumerateWithIndex()
{

169
src/Numerics/LinearAlgebra/Builder.cs

@ -33,6 +33,7 @@ using System.Linq;
using MathNet.Numerics.Distributions;
using MathNet.Numerics.LinearAlgebra.Solvers;
using MathNet.Numerics.LinearAlgebra.Storage;
using MathNet.Numerics.Properties;
using MathNet.Numerics.Random;
namespace MathNet.Numerics.LinearAlgebra.Double
@ -1181,6 +1182,174 @@ namespace MathNet.Numerics.LinearAlgebra
return m;
}
// Representation of Sparse Matrix
//
// Matrix A = [ 0 b 0 h 0 0 ]
// [ a c e i 0 0 ]
// [ 0 0 f j l n ]
// [ 0 d g k m 0 ]
//
// Rows = 4, Columns = 6, NonZeroCount = 14
//
// (1) COO, Coordinate 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:
// 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 }
/// <summary>
/// Create a new sparse matrix from a coordinate format.
/// This new matrix will be independent from the given arrays.
/// A new memory block will be allocated for storing the matrix.
/// </summary>
public Matrix<T> 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)
{
var message = string.Format(Resources.ArgumentArrayWrongLength, nonZeroCount);
throw new Exception(message);
}
// convert from COO to CSR
var csrValues = new T[nonZeroCount];
var csrColumnIndices = new int[nonZeroCount];
var csrRowPointers = new int[rows + 1];
for (int i = 0; i < nonZeroCount; i++)
{
csrRowPointers[cooRowIndices[i] + 1]++;
}
for (int i = 1; i < rows + 1; i++)
{
csrRowPointers[i] += csrRowPointers[i - 1];
}
var curr = new int[rows];
for (int i = 0; i < nonZeroCount; i++)
{
int row = cooRowIndices[i];
var loc = csrRowPointers[row] + curr[row];
curr[row]++;
csrColumnIndices[loc] = cooColumnIndices[i];
csrValues[loc] = cooValues[i];
}
var storage = new SparseCompressedRowMatrixStorage<T>(rows, columns, csrRowPointers, csrColumnIndices, csrValues);
return Sparse(storage);
}
/// <summary>
/// Create a new sparse matrix from a compressed sparse row format.
/// This new matrix will be independent from the given arrays.
/// A new memory block will be allocated for storing the matrix.
/// </summary>
public Matrix<T> 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)
{
var message = string.Format(Resources.ArgumentArrayWrongLength, rows + 1);
throw new Exception(message);
}
if (nonZeroCount != csrRowPointers[rows])
{
var message = string.Format("{0} should be same to {1}", nameof(nonZeroCount), csrRowPointers[rows]);
throw new Exception(message);
}
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);
var storage = new SparseCompressedRowMatrixStorage<T>(rows, columns, rowPointers, columnIndices, values);
return Sparse(storage);
}
/// <summary>
/// Create a new sparse matrix from a compressed sparse column format.
/// This new matrix will be independent from the given arrays.
/// A new memory block will be allocated for storing the matrix.
/// </summary>
public Matrix<T> 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)
{
var message = string.Format(Resources.ArgumentArrayWrongLength, columns + 1);
throw new Exception(message);
}
if (nonZeroCount != cscColumnPointers[columns])
{
var message = string.Format("{0} should be same to {1}", nameof(nonZeroCount), cscColumnPointers[columns]);
throw new Exception(message);
}
// convert from CSC to CSR
var csrValues = new T[nonZeroCount];
var csrRowPointers = new int[rows + 1];
var csrColumnIndices = new int[nonZeroCount];
for (int i = 0; i < columns; i++)
{
for (int j = cscColumnPointers[i]; j < cscColumnPointers[i + 1]; j++)
{
csrRowPointers[cscRowIndices[j] + 1]++;
}
}
for (int i = 1; i < rows + 1; i++)
{
csrRowPointers[i] += csrRowPointers[i - 1];
}
var curr = new int[rows];
for (int i = 0; i < columns; i++)
{
for (int j = cscColumnPointers[i]; j < cscColumnPointers[i + 1]; j++)
{
var loc = csrRowPointers[cscRowIndices[j]] + curr[cscRowIndices[j]];
curr[cscRowIndices[j]]++;
csrColumnIndices[loc] = i;
csrValues[loc] = cscValues[j];
}
}
var storage = new SparseCompressedRowMatrixStorage<T>(rows, columns, csrRowPointers, csrColumnIndices, csrValues);
return Sparse(storage);
}
/// <summary>
/// Create a new diagonal matrix straight from an initialized matrix storage instance.
/// The storage is used directly without copying.

8
src/Numerics/LinearAlgebra/Storage/SparseCompressedRowMatrixStorage.cs

@ -82,6 +82,14 @@ namespace MathNet.Numerics.LinearAlgebra.Storage
Values = new T[0];
}
internal SparseCompressedRowMatrixStorage(int rows, int columns, int[] rowPointers, int[] columnIndices, T[] values)
: base(rows, columns)
{
RowPointers = rowPointers;
ColumnIndices = columnIndices;
Values = values;
}
/// <summary>
/// True if the matrix storage format is dense.
/// </summary>

Loading…
Cancel
Save