# Linear Algebra

> USearch for C#

### Sparse Multiplication

```csharp
DenseProducts products = index.On(pool).Multiply(RowsView.RowsOf<float>(left, dimensions: 3), arena, wanted: 64);
ReadOnlySpan<Key> reached = products.Keys(0);         // Every indexed entry the first walk scored
ReadOnlySpan<double> dots = products.Scores(0);       // Paired with those keys, in traversal order
```

Every pair a traversal scored is an exact product the caller would otherwise recompute, so `Multiply` hands them all back, ragged per left row, with their offsets in the arena beside them.
It runs on the inner-product kinds alone, and the pairs a walk never reaches are the low and negative products.

### Late Interaction

```csharp
DenseOptions colbert = DenseOptions.Default with
{
    Dimensions = 128,
    Metric = DenseMetric.Inner,      // Only the inner-product kinds score logits
    Quantization = Scalar.BF16,
    ValuesPerKey = 32,               // A document is one key holding its token rows
};

DenseProducts scored = index.On(pool).Maxsim(RowsView.RowsOf<float>(queryTokens, dimensions: 128), rowsPerQuery: 32, wanted: 10, arena);
ReadOnlySpan<Key> documents = scored.Keys(0);         // The first query's best documents, largest first
ReadOnlySpan<double> totals = scored.Scores(0);       // Each document's summed maxima
```

Every query row takes its best match within a key, and those maxima sum to that key's score, so the wanted count is the "k" over documents rather than over rows.
Each query lands a ragged run in the arena, the same `DenseProducts` that `Multiply` returns, and a wanted count of zero keeps every document the query scored.
That is what a values-per-key bound above one exists for, and a graph frozen under a distance kind refuses with `Status.ConfigMismatch`.

### Attention

`Attend` is Flash Attention folded into the walk: every key it scores folds into each head's running maximum, partition and weighted value sum, with no top-k cut, and the outputs come back already divided by the partition.
The index stores no values, so every scored key resolves to a row of a values `RowsView` the caller lends, as wide as the keys and stored in the index's own quantization.

```csharp
DenseSoftmaxState[] carry = new DenseSoftmaxState[queryRows];
DenseVectors attended = index.On(pool).Attend(
    RowsView.RowsOf<float>(queryHeads, 128), RowsView.RowsOf<ushort>(valueCache, 128, Scalar.BF16),
    rowOf: key => positionOf[key],                       // Runs on the executor's threads together
    arena, headCount: 8, carry: carry);                  // One row per query row, as the queries' kind
ReadOnlySpan<float> first = attended.Row<float>(0);
```

With `headCount: 8`, row `t * 8 + h` of the queries is head `h` of token `t`, and the heads of one token share its walk.
A key resolving outside the values, or values of another kind or width than the library reads, refuses the call with `Status.ShapeMismatch`, since a missing row is a wrong softmax rather than a smaller one.
The `carry` holds one `DenseSoftmaxState` per query row, which folds this branch into another over the same queries, such as a recent window kept out of the index; it is state the caller carries between calls, so it stays lent rather than written into the arena.
An `outputsScalar` argument lands the attended rows as another kind than the queries' own.
`rowOf` is a host function, so it serves RAM handles alone: a VRAM walk resolves rows through a device function, which this binding does not build, and an `Attend` into a VRAM arena refuses with `Status.Device`.

