Abstractions csrmatrix multiply - CyrilB1531/lodestar GitHub Wiki
Development build. This page describes
main, not a released package. The latest published Lodestar.Abstractions is 0.2.0 — read its documentation.
Home › Abstractions › The shared sparse primitive
The matrix times a dense vector, or times a dense block, in one pass over the stored cells.
public double[] Multiply(ReadOnlySpan<double> vector)
public double[] Multiply(ReadOnlySpan<double> block, int columnCount)Parameters — vector is the dense vector to multiply by, and must hold exactly ColumnCount
values. The second overload takes block, a row-major dense operand of ColumnCount rows and
columnCount columns, so its length is the product of the two.
Returns — double[]. From the first overload, RowCount entries, each the dot product of that
row with vector. From the second, RowCount × columnCount entries, row-major.
Exceptions — ArgumentException when vector is not ColumnCount long, or when block is
not ColumnCount rows of columnCount. ArgumentOutOfRangeException when columnCount is not
positive, or when the product would not fit in a single array.
Example — multiplying by all ones totals each row, which is that document's term count.
using Lodestar.Abstractions;
using Lodestar.Text.Vectorization;
string[] docs = ["the cat eats", "the dog eats", "the cat and the dog"];
CsrMatrix counts = new CountVectorizer().FitTransform(docs);
double[] totals = counts.Multiply([1, 1, 1, 1, 1]);
double first = totals[0]; // => 3
double third = totals[2]; // => 5Remarks — the cost is O(RowCount + NonZeroCount), not RowCount × ColumnCount, which is the
whole reason to keep the matrix sparse. The third document totals 5 rather than 4 because the appears
twice in it and the count is a count.
This is the operation behind scoring a corpus against a linear model: the weights are the vector, and each row's dot product is that document's score.
A NaN or infinite value, stored or in the operand, is carried through, not refused — and a stored
zero times an infinity is NaN, as it is in scipy's product. One in the operand reaches the result
only where a row stores that column — the vector's entry, or the block's row, is otherwise never
read — so a row storing 1 at column 0 and nothing else, times [2, NaN], is 2, as in scipy.
The block overload is not a loop over the vector one. It makes one pass over the non-zeros
rather than columnCount passes: each column index is read once and the inner loop walks
contiguous memory on both sides. That is the operation a randomized SVD's power iteration spends
its time in — A Ω for a thin dense Ω — and
TransposeMultiply is its other half.
Before .NET 6 the cap is lower. The bound is the running runtime's largest array: .NET 6's
Array.MaxLength, or 0x7FEFFFFF elements earlier, where a result between the two is refused as
past it rather than failing in the allocation
(#1614). .NET Framework also refuses a
double array past 2 GB without gcAllowVeryLargeObjects, with its own out-of-memory error.
Applies to — net10.0, netstandard2.0.
See also — CsrMatrix.TransposeMultiply, CsrMatrix, CsrMatrix.ToDense.