# Matrix Kernels

> NumKong for Rust

Packed kernels are the main "matrix throughput" path in the crate.
They are `GEMM`-like in workload shape.
They are not a thin BLAS clone.

```rust
use numkong::{DotsPackedMatrix, Tensor};

let a = Tensor::<f32>::full(&[1024, 512], 1.0).unwrap();
let b = Tensor::<f32>::full(&[256, 512], 1.0).unwrap();

let b_packed = DotsPackedMatrix::new(&b).unwrap();
let c = a.dots_packed(&b_packed).unwrap();

assert_eq!(c.shape(), &[1024, 256]);
```

The useful economics are:

- pack `B` once
- reuse it across many `A` batches
- convert or pad once during packing instead of on every multiply
- reuse precomputed norms for `angulars_packed` and `euclideans_packed`

The crate checks row contiguity because these kernels assume contiguous rows.
Caller-side source alignment is not required.
The owned packed buffer handles its own aligned allocation internally.

### Symmetric Kernels for SYRK-Like Workloads

Symmetric kernels are for self-similarity and self-distance.
They are `SYRK`-like in shape.
They avoid duplicate `(i, j)` and `(j, i)` work.

```rust
use numkong::Tensor;

let vectors = Tensor::<f32>::full(&[100, 768], 1.0).unwrap();
let gram = vectors.view().dots_symmetric().unwrap();

assert_eq!(gram.shape(), &[100, 100]);
```

This family is also where row-window partitioning becomes the natural parallel model.
That is structurally different from packed `GEMM`-style work against a shared packed RHS.

`use numkong::Dots;`

`use numkong::DotsPackedMatrix;`

`use numkong::DotsPackedOps;`

`use numkong::SymmetricDotsOps;`

`use numkong::Angulars;`

`use numkong::AngularsPackedOps;`

`use numkong::Euclideans;`

`use numkong::EuclideansPackedOps;`

`use numkong::SymmetricAngularsOps;`

`use numkong::SymmetricEuclideansOps;`

`use numkong::Hammings;`

`use numkong::HammingsPackedOps;`

`use numkong::Jaccards;`

`use numkong::JaccardsPackedOps;`

`use numkong::SymmetricHammingsOps;`

`use numkong::SymmetricJaccardsOps;`

`use numkong::Attention;`

`use numkong::AttentionPackedMatrix;`

`use numkong::AttentionRope;`

## Dots

Low-level trait for batched __dot product__ computation using pre-packed matrices.

```rust
pub trait Dots: StorageElement + Sealed
```

Given A ∈ ℝᵐˣᵏ and packed B ∈ ℝⁿˣᵏ, computes C ∈ ℝᵐˣⁿ where:
Cᵢⱼ = aᵢ · bⱼ

B is pre-packed into a backend-specific layout for optimal access; strides are all in bytes.

#### When to use

Reach for this trait, or the matching `Tensor::dots_packed*` wrappers, whenever you multiply
many query rows against the __same__ matrix of database rows: pre-packing B once amortises
layout-conversion cost across every subsequent query. The accumulator type is intentionally
widened to avoid precision loss — `f32 × f32 → f64`, `f16 × f16 → f32`, `i8 × i8 → i32`, `u8 × u8 → u32`, and so on; see each impl's [`Dots::Accumulator`](#Accumulator). On [`u1x8`](https://www.unum.cloud/docs/numkong/rust/scalar-types.md#u1x8) the multiply degenerates
to a bitwise AND and the accumulator counts set bits into a `u32`.

Errors: [`Error::KernelFailed`](https://www.unum.cloud/docs/numkong/rust/quickstart.md#KernelFailed) when the kernel refuses to run, like on a buffer packed under
other [`Capabilities`](https://www.unum.cloud/docs/numkong/rust/runtime-dispatch.md#Capabilities) than the current ones.

Implementors: - `impl [Dots](#Dots) for f32`
- `impl [Dots](#Dots) for f64`
- `impl [Dots](#Dots) for [f16](https://www.unum.cloud/docs/numkong/rust/scalar-types.md#f16)`
- `impl [Dots](#Dots) for [bf16](https://www.unum.cloud/docs/numkong/rust/scalar-types.md#bf16)`
- `impl [Dots](#Dots) for i8`
- `impl [Dots](#Dots) for u8`
- `impl [Dots](#Dots) for [e4m3](https://www.unum.cloud/docs/numkong/rust/scalar-types.md#e4m3)`
- `impl [Dots](#Dots) for [e5m2](https://www.unum.cloud/docs/numkong/rust/scalar-types.md#e5m2)`
- `impl [Dots](#Dots) for [e2m3](https://www.unum.cloud/docs/numkong/rust/scalar-types.md#e2m3)`
- `impl [Dots](#Dots) for [e2m1x2](https://www.unum.cloud/docs/numkong/rust/scalar-types.md#e2m1x2)`
- `impl [Dots](#Dots) for [e3m2](https://www.unum.cloud/docs/numkong/rust/scalar-types.md#e3m2)`
- `impl [Dots](#Dots) for [u4x2](https://www.unum.cloud/docs/numkong/rust/scalar-types.md#u4x2)`
- `impl [Dots](#Dots) for [i4x2](https://www.unum.cloud/docs/numkong/rust/scalar-types.md#i4x2)`
- `impl [Dots](#Dots) for [u1x8](https://www.unum.cloud/docs/numkong/rust/scalar-types.md#u1x8)`

## Accumulator

Accumulator type for the multiplication.

```rust
type Accumulator: StorageElement;
```

## dots_pack_size

Returns the size in bytes needed for the packed B matrix buffer.

```rust
fn dots_pack_size(columns: usize, depth: usize) -> Result<usize, Error>
```

## dots_packed_shape

Reads the packed B matrix's columns and depth from its header. # Safety [`packed`](https://www.unum.cloud/docs/numkong/rust/scalar-types.md#packed) must point to a buffer produced by [`dots_pack`](#dots_pack).

```rust
unsafe fn dots_packed_shape(packed: *const u8) -> Result<(usize, usize), Error>
```

## dots_pack

Packs the B matrix into an optimized backend-specific layout.

```rust
unsafe fn dots_pack(
    matrix: *const Self,
    columns: usize,
    depth: usize,
    matrix_stride: usize,
    packed: *mut u8,
    columns_begin: usize,
    columns_end: usize,
) -> Result<(), Error>
```

Packing fuses no
transposition, so a transposed B is transposed in a separate pass first.

Safety: - `b` must point to valid memory for `columns * depth` elements

- [`packed`](https://www.unum.cloud/docs/numkong/rust/scalar-types.md#packed) must point to a buffer of at least `dots_pack_size(columns, depth)` bytes

## dots_packed

Computes C = A × Bᵀ using packed B.

```rust
unsafe fn dots_packed(
    queries: *const Self,
    packed: *const u8,
    output: *mut Self::Accumulator,
    rows: usize,
    columns: usize,
    depth: usize,
    query_stride: usize,
    output_stride: usize,
) -> Result<(), Error>
```

Safety: - `a` must point to valid memory for `rows * depth` elements with given stride

- [`packed`](https://www.unum.cloud/docs/numkong/rust/scalar-types.md#packed) must be a buffer filled by [`dots_pack`](#dots_pack)

- `c` must point to valid memory for [`rows`](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#rows) × `columns` elements with given stride

## dots_symmetric

Computes C = A × Aᵀ where C is symmetric.

```rust
unsafe fn dots_symmetric(
    vectors: *const Self,
    vector_count: usize,
    depth: usize,
    stride: usize,
    result: *mut Self::Accumulator,
    result_stride: usize,
    rows_begin: usize,
    rows_end: usize,
) -> Result<(), Error>
```

Given input matrix A of shape __[n,k]__, computes the symmetric matrix of all pairwise dot
products. Only the upper triangle is computed, then mirrored to the lower triangle.

Safety: - [`vectors`](https://www.unum.cloud/docs/numkong/rust/maxsim.md#vectors) must point to valid memory for `vector_count × depth` elements with given stride

- `result` must point to valid memory for `vector_count×vector_count` elements, given stride

- Strides are in bytes, not elements

- `rows_begin <= rows_end`; `rows_end` is clamped to `vector_count`

## DotsPackedMatrix

Pre-packed B matrix for efficient repeated GEMM operations.

```rust
pub struct DotsPackedMatrix<Scalar: Dots, Alloc: Allocator = Global>
```

Uses raw memory allocation, not std::Vec, for maximum control.

When multiplying A × Bᵀ multiple times with the same B matrix, packing B once and reusing it is
much faster than packing each time.

#### Usage

For C = A × Bᵀ where B is __[n,k]__:

```
// Requires linking against libnumkong C library
let b_packed = DotsPackedMatrix::new(&#x26;b_array).unwrap();
let c = a_array.dots_packed(&#x26;b_packed).unwrap();
```

For C = A × B where B is __[k,n]__ in standard GEMM layout:

```
// Requires linking against libnumkong C library
let b_packed = DotsPackedMatrix::new_transposed(&#x26;b_array).unwrap();
let c = a_array.dots_packed(&#x26;b_packed).unwrap();
```

Implements: `Debug`, `Send`, `Sync`

## clone

Clone this packed matrix, returning an error on allocation failure.

```rust
pub fn clone(&self) -> Result<Self, Error>
```

## empty_in

An empty packed matrix owning no allocation, using a custom allocator.

```rust
pub fn empty_in(alloc: Alloc) -> Self
```

Fill it with [`pack_into`](https://www.unum.cloud/docs/numkong/rust/maxsim.md#pack_into).

## new_in

Packs the matrix into a buffer allocated by `alloc`.

```rust
pub fn new_in<Matrix, const MAX_RANK: usize>(matrix: &Matrix, alloc: Alloc) -> Result<Self, Error>
where
    Matrix: TensorRef<Scalar, MAX_RANK> + ?Sized,
```

## pack_into

Repack `b` into this matrix's existing buffer, reusing the allocation when the packed size fits [`capacity`](https://www.unum.cloud/docs/numkong/rust/maxsim.md#capacity) and reallocating through the stored allocator only when it must grow.

```rust
pub fn pack_into<Matrix, const MAX_RANK: usize>(&mut self, matrix: &Matrix) -> Result<(), Error>
where
    Matrix: TensorRef<Scalar, MAX_RANK> + ?Sized,
```

A
steady-state loop over same-shaped operands then allocates at most once. `b` must be 2D with
contiguous rows.

## reserve

Pre-grow the buffer to hold a columns-by-depth B matrix, so a later [`pack_into`](https://www.unum.cloud/docs/numkong/rust/maxsim.md#pack_into) that fits stays allocation-free with a stable pointer — hoist this out of a repeated-pack loop.

```rust
pub fn reserve(&mut self, columns: usize, depth: usize) -> Result<(), Error>
```

## new_transposed_in

Pack Bᵀ where B is __[k,n]__ row-major, the standard GEMM layout, using a custom allocator.

```rust
pub fn new_transposed_in<Matrix, const MAX_RANK: usize>(
    matrix: &Matrix,
    alloc: Alloc,
) -> Result<Self, Error>
where
    Matrix: TensorRef<Scalar, MAX_RANK> + ?Sized,
```

Materializes the transpose into a contiguous buffer, then packs normally.
Result computes: C = A × B

Returns `Err` if:

- b is not 2D

- b is a sub-byte type — transpose unsupported

- allocation fails

## *

Returns a reference to the allocator.

```rust
pub fn allocator(&self) -> &Alloc
```

## shape

Returns the shape __[columns,depth]__ of the original B matrix.

```rust
pub fn shape(&self) -> (usize, usize)
```

## pack_size

Bytes a packed buffer occupies for a columns-by-depth B matrix of this scalar type under the active backend's layout.

```rust
pub fn pack_size(columns: usize, depth: usize) -> Result<usize, Error>
```

Mirrors [`crate::attention::AttentionPackedMatrix::pack_size`](https://www.unum.cloud/docs/numkong/rust/mesh-alignment.md#pack_size) and
lets a caller pre-size an external buffer without packing.

## from_packed_bytes_in

Adopt an externally-produced packed buffer by copying `bytes` into a container-owned allocation tagged with the given `columns` and [`depth`](#depth).

```rust
pub unsafe fn from_packed_bytes_in(
    bytes: &[u8],
    columns: usize,
    depth: usize,
    alloc: Alloc,
) -> Result<Self, Error>
```

The dots packed layout is not
self-describing — it carries no dtype tag and its AMX variant drops the exact depth — so the
caller must supply the shape the bytes were packed for.

Safety: `bytes` must be a valid packing of a columns-by-depth matrix of [`Scalar`](https://www.unum.cloud/docs/numkong/rust/elementwise-and-reductions.md#Scalar), produced by this
build's packer for a compatible backend; anything else makes a later [`dots_packed`](#dots_packed) read out
of bounds or return garbage.

## capacity

Bytes currently allocated (>= the live packed size).

```rust
pub fn capacity(&self) -> usize
```

## *

Reset to logically empty, keeping the allocation so the next [`pack_into`](https://www.unum.cloud/docs/numkong/rust/maxsim.md#pack_into) reuses it.

```rust
pub fn clear(&mut self)
```

## as_*_bytes

Returns the packed data buffer.

```rust
pub fn as__bytes(&self) -> &[u8]
```

## as_*_ptr

Returns a pointer to the packed data.

```rust
pub fn as__ptr(&self) -> *const u8
```

## new

Pack B matrix where B is __[n,k]__ row-major using the global allocator.

```rust
pub fn new<Matrix, const MAX_RANK: usize>(matrix: &Matrix) -> Result<Self, Error>
where
    Matrix: TensorRef<Scalar, MAX_RANK> + ?Sized,
```

Result computes: C = A × Bᵀ

## new_transposed

Pack Bᵀ where B is __[k,n]__ row-major, standard GEMM layout, using the global allocator.

```rust
pub fn new_transposed<Matrix, const MAX_RANK: usize>(matrix: &Matrix) -> Result<Self, Error>
where
    Matrix: TensorRef<Scalar, MAX_RANK> + ?Sized,
```

Result computes: C = A × B

## DotsPackedOps

Extension trait: packed GEMM (`C = A × Bᵀ`) for any immutable tensor reference — owned [`Tensor`](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#Tensor), borrowed [`TensorView`](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#TensorView), or [`TensorSpan`](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#TensorSpan).

```rust
pub trait DotsPackedOps<Scalar: Dots, const MAX_RANK: usize>: TensorRef<Scalar, MAX_RANK>
```

Blanket-implemented for every [`TensorRef`](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#TensorRef), so an `A` operand backed by an mmap'd view can
multiply against a pre-packed [`DotsPackedMatrix`](#DotsPackedMatrix) without first materializing an owned copy.
The allocating entry point allocates its result through a clone of `self`'s allocator.

Implementors: - `impl<Scalar: [Dots](#Dots), const MAX_RANK: usize, A: [TensorRef](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#TensorRef)<Scalar, MAX_RANK>> [DotsPackedOps](#DotsPackedOps)<Scalar, MAX_RANK> for A`

## dots_packed

Dot-product multiply: C = self × packed_rightᵀ

```rust
fn dots_packed<PackedAlloc: Allocator>(
    &self,
    packed_right: &DotsPackedMatrix<Scalar, PackedAlloc>,
) -> Result<Tensor<Scalar::Accumulator, Self::Alloc, MAX_RANK>, Error>
where
    Self::Alloc: Clone,
```

self must be 2D __[m,k]__ with contiguous rows. packed_right contains B __[n,k]__
packed. Returns C __[m,n]__, allocated like `self`.

Returns `Err` if:

- self is not 2D

- self has non-contiguous rows

- inner dimensions don't match

- output allocation fails

- the kernel refuses `packed_right`, like one packed under other capabilities

## dots_packed_into

Dot-product multiply into an existing output, avoiding allocation.

```rust
fn dots_packed_into<PackedAlloc, OutputTensor, const OUTPUT_MAX_RANK: usize>(
    &self,
    packed_right: &DotsPackedMatrix<Scalar, PackedAlloc>,
    output: &mut OutputTensor,
) -> Result<(), Error>
where
    PackedAlloc: Allocator,
    OutputTensor: TensorMut<Scalar::Accumulator, OUTPUT_MAX_RANK>,
```

The output may be a `&mut Tensor<...>` or `&mut TensorSpan<...>`; any writable tensor
container that implements [`TensorMut`](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#TensorMut) works. The kernel overwrites `c` entirely, so it
need not arrive pre-initialized.

## SymmetricDotsOps

Extension trait: symmetric dot-product matrix for any [`TensorRef`](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#TensorRef) implementor.

```rust
pub trait SymmetricDotsOps<Scalar: Dots, const MAX_RANK: usize>: TensorRef<Scalar, MAX_RANK>
where
    Scalar::Accumulator: 'static,
```

Blanket-implemented for every `TensorRef<Scalar, R>`, so calling `vectors.dots_symmetric()`
works on both owned [`Tensor`](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#Tensor) and borrowed [`TensorView`](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#TensorView) / [`TensorSpan`](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#TensorSpan). Only the __upper
triangle__, including the diagonal, of the output is written by the kernel; the lower triangle
is queries untouched — callers that need a full dense matrix must mirror it.

Prefer this extension trait when you have a generic [`TensorRef`](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#TensorRef); use the inherent
[`TensorView::dots_symmetric`](#dots_symmetric) form instead when you already hold a view and want to skip
the extra trait import.

Implementors: - `impl<Scalar: [Dots](#Dots), const R: usize, OutputTensor: [TensorRef](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#TensorRef)<Scalar, R>> [SymmetricDotsOps](#SymmetricDotsOps)<Scalar, R> for OutputTensor where Scalar::Accumulator: 'static`

## dots_symmetric

Computes the upper triangle of the symmetric dot-product matrix of the rows with themselves.

```rust
fn dots_symmetric(&self) -> Result<Tensor<Scalar::Accumulator, Self::Alloc, MAX_RANK>, Error>
where
    Self::Alloc: Clone,
```

## dots_symmetric_into

Writes the symmetric dot-product matrix into pre-allocated output, touching only the upper triangle of it.

```rust
fn dots_symmetric_into<Out, const OUTPUT_MAX_RANK: usize>(
    &self,
    output: &mut Out,
) -> Result<(), Error>
where
    Out: TensorMut<Scalar::Accumulator, OUTPUT_MAX_RANK>,
```

## Angulars

Low-level trait for batched __angular distance__ operations.

```rust
pub trait Angulars: Dots
```

Given A ∈ ℝᵐˣᵏ and packed B ∈ ℝⁿˣᵏ, computes C ∈ ℝᵐˣⁿ where:
Cᵢⱼ = 1 − cos(θᵢⱼ) = 1 − (aᵢ · bⱼ) / (‖aᵢ‖ × ‖bⱼ‖)

Packing reuses [`Dots::dots_pack`](#dots_pack) for optimal memory layout.

#### When to use

Angular distance, also called cosine, is the standard similarity metric for embeddings from
neural language / vision models. The accumulator is widened during the dot-product step — `f32`
inputs accumulate in `f64`, [`f16`](https://www.unum.cloud/docs/numkong/rust/scalar-types.md#f16) / [`bf16`](https://www.unum.cloud/docs/numkong/rust/scalar-types.md#bf16) in `f32`, `i8` / `u8` in `i32` / `u32` — then
normalised back down to [`Self::SpatialResult`](#SpatialResult). Pre-packing the corpus is the standard pattern
for repeated-query retrieval.

Errors: [`Error::KernelFailed`](https://www.unum.cloud/docs/numkong/rust/quickstart.md#KernelFailed) when the kernel refuses to run, like on a buffer packed under
other [`Capabilities`](https://www.unum.cloud/docs/numkong/rust/runtime-dispatch.md#Capabilities) than the current ones.

Implementors: - `impl [Angulars](#Angulars) for f32`
- `impl [Angulars](#Angulars) for f64`
- `impl [Angulars](#Angulars) for [f16](https://www.unum.cloud/docs/numkong/rust/scalar-types.md#f16)`
- `impl [Angulars](#Angulars) for [bf16](https://www.unum.cloud/docs/numkong/rust/scalar-types.md#bf16)`
- `impl [Angulars](#Angulars) for i8`
- `impl [Angulars](#Angulars) for u8`
- `impl [Angulars](#Angulars) for [e4m3](https://www.unum.cloud/docs/numkong/rust/scalar-types.md#e4m3)`
- `impl [Angulars](#Angulars) for [e5m2](https://www.unum.cloud/docs/numkong/rust/scalar-types.md#e5m2)`
- `impl [Angulars](#Angulars) for [e2m3](https://www.unum.cloud/docs/numkong/rust/scalar-types.md#e2m3)`
- `impl [Angulars](#Angulars) for [e2m1x2](https://www.unum.cloud/docs/numkong/rust/scalar-types.md#e2m1x2)`
- `impl [Angulars](#Angulars) for [e3m2](https://www.unum.cloud/docs/numkong/rust/scalar-types.md#e3m2)`
- `impl [Angulars](#Angulars) for [u4x2](https://www.unum.cloud/docs/numkong/rust/scalar-types.md#u4x2)`
- `impl [Angulars](#Angulars) for [i4x2](https://www.unum.cloud/docs/numkong/rust/scalar-types.md#i4x2)`

## SpatialResult

Result type for angular distances.

```rust
type SpatialResult: StorageElement;
```

## angulars_packed

Computes angular distances between A rows and packed B columns.

```rust
unsafe fn angulars_packed(
    queries: *const Self,
    packed: *const u8,
    output: *mut Self::SpatialResult,
    rows: usize,
    columns: usize,
    depth: usize,
    query_stride: usize,
    output_stride: usize,
) -> Result<(), Error>
```

Safety: - `a` must point to valid memory for `rows * depth` elements with given stride

- [`packed`](https://www.unum.cloud/docs/numkong/rust/scalar-types.md#packed) must be a buffer filled by [`Dots::dots_pack`](#dots_pack)

- `c` must point to valid memory for [`rows`](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#rows) × `columns` result elements with given stride

## angulars_symmetric

Computes symmetric angular distance matrix.

```rust
unsafe fn angulars_symmetric(
    vectors: *const Self,
    vector_count: usize,
    depth: usize,
    stride: usize,
    result: *mut Self::SpatialResult,
    result_stride: usize,
    rows_begin: usize,
    rows_end: usize,
) -> Result<(), Error>
```

Safety: - [`vectors`](https://www.unum.cloud/docs/numkong/rust/maxsim.md#vectors) must point to valid memory for `vector_count * depth` elements

- `result` must point to valid memory for `vector_count * vector_count` result elements

## AngularsPackedOps

Extension trait: packed angular distances (`C = angular(A, Bᵀ)`) for any immutable tensor reference — owned [`Tensor`](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#Tensor), borrowed [`TensorView`](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#TensorView), or [`TensorSpan`](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#TensorSpan).

```rust
pub trait AngularsPackedOps<Scalar: Angulars, const MAX_RANK: usize>: TensorRef<Scalar, MAX_RANK>
```

Blanket-implemented for every [`TensorRef`](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#TensorRef), so an `A` operand backed by an mmap'd view can
score against a pre-packed [`DotsPackedMatrix`](#DotsPackedMatrix) without first materializing an owned copy. The
allocating entry point allocates its result through a clone of `self`'s allocator.

Implementors: - `impl<Scalar: [Angulars](#Angulars), const MAX_RANK: usize, A: [TensorRef](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#TensorRef)<Scalar, MAX_RANK>> [AngularsPackedOps](#AngularsPackedOps)<Scalar, MAX_RANK> for A`

## angulars_packed

Angular distances: C = angular(self, packed_rightᵀ)

```rust
fn angulars_packed<PackedAlloc: Allocator>(
    &self,
    packed_right: &DotsPackedMatrix<Scalar, PackedAlloc>,
) -> Result<Tensor<Scalar::SpatialResult, Self::Alloc, MAX_RANK>, Error>
where
    Self::Alloc: Clone,
```

self must be 2D __[m,k]__ with contiguous rows. packed_right contains B __[n,k]__
packed. Returns C __[m,n]__, allocated like `self`.

Returns `Err` if:

- self is not 2D

- self has non-contiguous rows

- inner dimensions don't match

- output allocation fails

- the kernel refuses `packed_right`, like one packed under other capabilities

## angulars_packed_into

Angular distances into an existing output, avoiding allocation.

```rust
fn angulars_packed_into<PackedAlloc, OutputTensor, const OUTPUT_MAX_RANK: usize>(
    &self,
    packed_right: &DotsPackedMatrix<Scalar, PackedAlloc>,
    output: &mut OutputTensor,
) -> Result<(), Error>
where
    PackedAlloc: Allocator,
    OutputTensor: TensorMut<Scalar::SpatialResult, OUTPUT_MAX_RANK>,
```

The output may be a `&mut Tensor<...>` or `&mut TensorSpan<...>`; any writable tensor
container that implements [`TensorMut`](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#TensorMut) works. The kernel overwrites `c` entirely, so it
need not arrive pre-initialized.

## Euclideans

Low-level trait for batched __euclidean distance__ operations.

```rust
pub trait Euclideans: Dots
```

Given A ∈ ℝᵐˣᵏ and packed B ∈ ℝⁿˣᵏ, computes C ∈ ℝᵐˣⁿ where:
Cᵢⱼ = √(max(0, ‖aᵢ‖² + ‖bⱼ‖² − 2 · aᵢ · bⱼ))

Packing reuses [`Dots::dots_pack`](#dots_pack) for optimal memory layout.

#### When to use

Use for ℓ₂ distance in k-NN / clustering / metric learning. The kernel computes `‖a‖² + ‖b‖² − 2 a·b` in the widened accumulator domain (`f32 × f32 → f64`, `f16 × f16 → f32`, `i8 × i8 → i32`,
…) before the square-root, so numerical cancellation is bounded. Pre-pack the reference matrix
once when queries are repeated against the same corpus.

Errors: [`Error::KernelFailed`](https://www.unum.cloud/docs/numkong/rust/quickstart.md#KernelFailed) when the kernel refuses to run, like on a buffer packed under
other [`Capabilities`](https://www.unum.cloud/docs/numkong/rust/runtime-dispatch.md#Capabilities) than the current ones.

Implementors: - `impl [Euclideans](#Euclideans) for f32`
- `impl [Euclideans](#Euclideans) for f64`
- `impl [Euclideans](#Euclideans) for [f16](https://www.unum.cloud/docs/numkong/rust/scalar-types.md#f16)`
- `impl [Euclideans](#Euclideans) for [bf16](https://www.unum.cloud/docs/numkong/rust/scalar-types.md#bf16)`
- `impl [Euclideans](#Euclideans) for i8`
- `impl [Euclideans](#Euclideans) for u8`
- `impl [Euclideans](#Euclideans) for [e4m3](https://www.unum.cloud/docs/numkong/rust/scalar-types.md#e4m3)`
- `impl [Euclideans](#Euclideans) for [e5m2](https://www.unum.cloud/docs/numkong/rust/scalar-types.md#e5m2)`
- `impl [Euclideans](#Euclideans) for [e2m3](https://www.unum.cloud/docs/numkong/rust/scalar-types.md#e2m3)`
- `impl [Euclideans](#Euclideans) for [e2m1x2](https://www.unum.cloud/docs/numkong/rust/scalar-types.md#e2m1x2)`
- `impl [Euclideans](#Euclideans) for [e3m2](https://www.unum.cloud/docs/numkong/rust/scalar-types.md#e3m2)`
- `impl [Euclideans](#Euclideans) for [u4x2](https://www.unum.cloud/docs/numkong/rust/scalar-types.md#u4x2)`
- `impl [Euclideans](#Euclideans) for [i4x2](https://www.unum.cloud/docs/numkong/rust/scalar-types.md#i4x2)`

## SpatialResult

Result type for euclidean distances.

```rust
type SpatialResult: StorageElement;
```

## euclideans_packed

Computes euclidean distances between A rows and packed B columns.

```rust
unsafe fn euclideans_packed(
    queries: *const Self,
    packed: *const u8,
    output: *mut Self::SpatialResult,
    rows: usize,
    columns: usize,
    depth: usize,
    query_stride: usize,
    output_stride: usize,
) -> Result<(), Error>
```

Safety: - `a` must point to valid memory for `rows * depth` elements with given stride

- [`packed`](https://www.unum.cloud/docs/numkong/rust/scalar-types.md#packed) must be a buffer filled by [`Dots::dots_pack`](#dots_pack)

- `c` must point to valid memory for [`rows`](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#rows) × `columns` result elements with given stride

## euclideans_symmetric

Computes symmetric euclidean distance matrix.

```rust
unsafe fn euclideans_symmetric(
    vectors: *const Self,
    vector_count: usize,
    depth: usize,
    stride: usize,
    result: *mut Self::SpatialResult,
    result_stride: usize,
    rows_begin: usize,
    rows_end: usize,
) -> Result<(), Error>
```

Safety: - [`vectors`](https://www.unum.cloud/docs/numkong/rust/maxsim.md#vectors) must point to valid memory for `vector_count * depth` elements

- `result` must point to valid memory for `vector_count * vector_count` result elements

## EuclideansPackedOps

Extension trait: packed euclidean distances (`C = euclidean(A, Bᵀ)`) for any immutable tensor reference — owned [`Tensor`](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#Tensor), borrowed [`TensorView`](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#TensorView), or [`TensorSpan`](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#TensorSpan).

```rust
pub trait EuclideansPackedOps<Scalar: Euclideans, const MAX_RANK: usize>: TensorRef<Scalar, MAX_RANK>
```

Blanket-implemented for every [`TensorRef`](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#TensorRef), so an `A` operand backed by an mmap'd view can
score against a pre-packed [`DotsPackedMatrix`](#DotsPackedMatrix) without first materializing an owned copy. The
allocating entry point allocates its result through a clone of `self`'s allocator.

Implementors: - `impl<Scalar: [Euclideans](#Euclideans), const MAX_RANK: usize, A: [TensorRef](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#TensorRef)<Scalar, MAX_RANK>> [EuclideansPackedOps](#EuclideansPackedOps)<Scalar, MAX_RANK> for A`

## euclideans_packed

Euclidean distances: C = euclidean(self, packed_rightᵀ)

```rust
fn euclideans_packed<PackedAlloc: Allocator>(
    &self,
    packed_right: &DotsPackedMatrix<Scalar, PackedAlloc>,
) -> Result<Tensor<Scalar::SpatialResult, Self::Alloc, MAX_RANK>, Error>
where
    Self::Alloc: Clone,
```

self must be 2D __[m,k]__ with contiguous rows. packed_right contains B __[n,k]__
packed. Returns C __[m,n]__, allocated like `self`.

Returns `Err` if:

- self is not 2D

- self has non-contiguous rows

- inner dimensions don't match

- output allocation fails

- the kernel refuses `packed_right`, like one packed under other capabilities

## euclideans_packed_into

Euclidean distances into an existing output, avoiding allocation.

```rust
fn euclideans_packed_into<PackedAlloc, OutputTensor, const OUTPUT_MAX_RANK: usize>(
    &self,
    packed_right: &DotsPackedMatrix<Scalar, PackedAlloc>,
    output: &mut OutputTensor,
) -> Result<(), Error>
where
    PackedAlloc: Allocator,
    OutputTensor: TensorMut<Scalar::SpatialResult, OUTPUT_MAX_RANK>,
```

The output may be a `&mut Tensor<...>` or `&mut TensorSpan<...>`; any writable tensor
container that implements [`TensorMut`](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#TensorMut) works. The kernel overwrites `c` entirely, so it
need not arrive pre-initialized.

## SymmetricAngularsOps

Extension trait: symmetric angular-distance matrix for any [`TensorRef`](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#TensorRef) implementor.

```rust
pub trait SymmetricAngularsOps<Scalar: Angulars, const MAX_RANK: usize>: TensorRef<Scalar, MAX_RANK>
```

Implementors: - `impl<Scalar: [Angulars](#Angulars), const R: usize, OutputTensor: [TensorRef](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#TensorRef)<Scalar, R>> [SymmetricAngularsOps](#SymmetricAngularsOps)<Scalar, R> for OutputTensor`

## angulars_symmetric

Computes the upper triangle of the symmetric angular-distance matrix of the rows with themselves.

```rust
fn angulars_symmetric(&self) -> Result<Tensor<Scalar::SpatialResult, Self::Alloc, MAX_RANK>, Error>
where
    Self::Alloc: Clone,
```

## angulars_symmetric_into

Writes the symmetric angular-distance matrix into pre-allocated output, touching only the upper triangle of it.

```rust
fn angulars_symmetric_into<Out, const OUTPUT_MAX_RANK: usize>(
    &self,
    output: &mut Out,
) -> Result<(), Error>
where
    Out: TensorMut<Scalar::SpatialResult, OUTPUT_MAX_RANK>,
```

## SymmetricEuclideansOps

Extension trait: symmetric euclidean distance matrix for any [`TensorRef`](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#TensorRef) implementor.

```rust
pub trait SymmetricEuclideansOps<Scalar: Euclideans, const MAX_RANK: usize>: TensorRef<Scalar, MAX_RANK>
```

Blanket-implemented for every `TensorRef<Scalar, R>`, which means
`vectors.euclideans_symmetric()` compiles whether [`vectors`](https://www.unum.cloud/docs/numkong/rust/maxsim.md#vectors) is an owned [`Tensor`](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#Tensor) or a
borrowed view. The kernel only writes the upper triangle, including the diagonal — the lower
triangle is queries alone and callers should mirror it themselves if required.

Prefer this trait when working through a generic [`TensorRef`](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#TensorRef); reach for the inherent
[`TensorView::euclideans_symmetric`](#euclideans_symmetric) method when you already hold a view.

Implementors: - `impl<Scalar: [Euclideans](#Euclideans), const R: usize, OutputTensor: [TensorRef](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#TensorRef)<Scalar, R>> [SymmetricEuclideansOps](#SymmetricEuclideansOps)<Scalar, R> for OutputTensor`

## euclideans_symmetric

Computes the upper triangle of the symmetric Euclidean-distance matrix of the rows with themselves.

```rust
fn euclideans_symmetric(
    &self,
) -> Result<Tensor<Scalar::SpatialResult, Self::Alloc, MAX_RANK>, Error>
where
    Self::Alloc: Clone,
```

## euclideans_symmetric_into

Writes the symmetric euclidean-distance matrix into pre-allocated output, touching only the upper triangle of it.

```rust
fn euclideans_symmetric_into<Out, const OUTPUT_MAX_RANK: usize>(
    &self,
    output: &mut Out,
) -> Result<(), Error>
where
    Out: TensorMut<Scalar::SpatialResult, OUTPUT_MAX_RANK>,
```

## Hammings

Low-level trait for batched __Hamming distance__ operations.

```rust
pub trait Hammings: Dots
```

Given A ∈ {0,1}ᵐˣᵏ and packed B ∈ {0,1}ⁿˣᵏ, computes C ∈ ℕᵐˣⁿ where:
Cᵢⱼ = popcount(aᵢ ⊕ bⱼ)

Packing is inherited from the [`Dots`](#Dots) supertrait.

#### When to use

Binary feature vectors, bit-packed into [`u1x8`](https://www.unum.cloud/docs/numkong/rust/scalar-types.md#u1x8), are common in approximate nearest-neighbour
search and bloom-filter-style retrieval. Packing the query set once and running Hamming distance
against many candidate rows is the hot path. Results accumulate in `u32`, wide enough for any
practical binary vector length.

Errors: [`Error::KernelFailed`](https://www.unum.cloud/docs/numkong/rust/quickstart.md#KernelFailed) when the kernel refuses to run, like on a buffer packed under
other [`Capabilities`](https://www.unum.cloud/docs/numkong/rust/runtime-dispatch.md#Capabilities) than the current ones.

Implementors: - `impl [Hammings](#Hammings) for [u1x8](https://www.unum.cloud/docs/numkong/rust/scalar-types.md#u1x8)`

## hammings_packed

Computes Hamming distances between values matrix rows and packed query rows.

```rust
unsafe fn hammings_packed(
    queries: *const Self,
    packed: *const u8,
    result: *mut u32,
    rows: usize,
    columns: usize,
    depth: usize,
    v_stride: usize,
    r_stride: usize,
) -> Result<(), Error>
```

Safety: - `a` must point to valid memory for the values matrix

- [`packed`](https://www.unum.cloud/docs/numkong/rust/scalar-types.md#packed) must be a buffer filled by [`Dots::dots_pack`](#dots_pack)

- `result` must point to valid memory for [`rows`](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#rows) × `columns` u32 elements

## hammings_symmetric

Computes symmetric Gram matrix of Hamming distances: C = A × Aᵀ.

```rust
unsafe fn hammings_symmetric(
    vectors: *const Self,
    vector_count: usize,
    depth: usize,
    stride: usize,
    result: *mut u32,
    result_stride: usize,
    rows_begin: usize,
    rows_end: usize,
) -> Result<(), Error>
```

Safety: - [`vectors`](https://www.unum.cloud/docs/numkong/rust/maxsim.md#vectors) must point to valid memory for the input matrix

- `result` must point to valid memory for `vector_count * vector_count` u32 elements

## HammingsPackedOps

Extension trait: packed Hamming distances (`C = hamming(A, Bᵀ)`) for any immutable tensor reference — owned [`Tensor`](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#Tensor), borrowed [`TensorView`](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#TensorView), or [`TensorSpan`](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#TensorSpan).

```rust
pub trait HammingsPackedOps<Scalar: Hammings, const MAX_RANK: usize>: TensorRef<Scalar, MAX_RANK>
```

Blanket-implemented for every [`TensorRef`](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#TensorRef), so an `A` operand backed by an mmap'd view can
score against a pre-packed [`DotsPackedMatrix`](#DotsPackedMatrix) without first materializing an owned copy. The
allocating entry point allocates its result through a clone of `self`'s allocator.

Implementors: - `impl<Scalar: [Hammings](#Hammings), const MAX_RANK: usize, A: [TensorRef](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#TensorRef)<Scalar, MAX_RANK>> [HammingsPackedOps](#HammingsPackedOps)<Scalar, MAX_RANK> for A`

## hammings_packed

Hamming distances: C = self × packed_rightᵀ

```rust
fn hammings_packed<PackedAlloc: Allocator>(
    &self,
    packed_right: &DotsPackedMatrix<Scalar, PackedAlloc>,
) -> Result<Tensor<u32, Self::Alloc, MAX_RANK>, Error>
where
    Self::Alloc: Clone,
```

self must be 2D __[m,k]__ with contiguous rows. packed_right contains B __[n,k]__
packed. Returns C __[m,n]__, allocated like `self`.

Returns `Err` if:

- self is not 2D

- self has non-contiguous rows

- inner dimensions don't match

- output allocation fails

- the kernel refuses `packed_right`, like one packed under other capabilities

## hammings_packed_into

Hamming distances into an existing output, avoiding allocation.

```rust
fn hammings_packed_into<PackedAlloc, OutputTensor, const OUTPUT_MAX_RANK: usize>(
    &self,
    packed_right: &DotsPackedMatrix<Scalar, PackedAlloc>,
    output: &mut OutputTensor,
) -> Result<(), Error>
where
    PackedAlloc: Allocator,
    OutputTensor: TensorMut<u32, OUTPUT_MAX_RANK>,
```

The output may be a `&mut Tensor<...>` or `&mut TensorSpan<...>`; any writable tensor
container that implements [`TensorMut`](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#TensorMut) works. The kernel overwrites `c` entirely, so it
need not arrive pre-initialized.

## Jaccards

Low-level trait for batched __Jaccard distance__ operations.

```rust
pub trait Jaccards: Dots
```

Given A ∈ {0,1}ᵐˣᵏ and packed B ∈ {0,1}ⁿˣᵏ, computes C ∈ ℝᵐˣⁿ where:
Cᵢⱼ = 1 − popcount(aᵢ ∧ bⱼ) / popcount(aᵢ ∨ bⱼ)

Packing is inherited from the [`Dots`](#Dots) supertrait.

#### When to use

Jaccard distance measures set dissimilarity and is the natural metric for binary feature
presence/absence. Pack the query set once and query many candidates in a single batched kernel
call. The result type is `f32` because the ratio is inherently fractional.

Errors: [`Error::KernelFailed`](https://www.unum.cloud/docs/numkong/rust/quickstart.md#KernelFailed) when the kernel refuses to run, like on a buffer packed under
other [`Capabilities`](https://www.unum.cloud/docs/numkong/rust/runtime-dispatch.md#Capabilities) than the current ones.

Implementors: - `impl [Jaccards](#Jaccards) for [u1x8](https://www.unum.cloud/docs/numkong/rust/scalar-types.md#u1x8)`

## JaccardResult

Result type for Jaccard distances.

```rust
type JaccardResult: StorageElement;
```

## jaccards_packed

Computes Jaccard distances between values matrix rows and packed query rows.

```rust
unsafe fn jaccards_packed(
    queries: *const Self,
    packed: *const u8,
    result: *mut Self::JaccardResult,
    rows: usize,
    columns: usize,
    depth: usize,
    v_stride: usize,
    r_stride: usize,
) -> Result<(), Error>
```

Safety: - `a` must point to valid memory for the values matrix

- [`packed`](https://www.unum.cloud/docs/numkong/rust/scalar-types.md#packed) must be a buffer filled by [`Dots::dots_pack`](#dots_pack)

- `result` must point to valid memory for [`rows`](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#rows) × `columns` elements

## jaccards_symmetric

Computes symmetric Gram matrix of Jaccard distances.

```rust
unsafe fn jaccards_symmetric(
    vectors: *const Self,
    vector_count: usize,
    depth: usize,
    stride: usize,
    result: *mut Self::JaccardResult,
    result_stride: usize,
    rows_begin: usize,
    rows_end: usize,
) -> Result<(), Error>
```

Safety: - [`vectors`](https://www.unum.cloud/docs/numkong/rust/maxsim.md#vectors) must point to valid memory for the input matrix

- `result` must point to valid memory for `vector_count * vector_count` elements

## JaccardsPackedOps

Extension trait: packed Jaccard distances (`C = jaccard(A, Bᵀ)`) for any immutable tensor reference — owned [`Tensor`](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#Tensor), borrowed [`TensorView`](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#TensorView), or [`TensorSpan`](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#TensorSpan).

```rust
pub trait JaccardsPackedOps<Scalar: Jaccards, const MAX_RANK: usize>: TensorRef<Scalar, MAX_RANK>
```

Blanket-implemented for every [`TensorRef`](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#TensorRef), so an `A` operand backed by an mmap'd view can
score against a pre-packed [`DotsPackedMatrix`](#DotsPackedMatrix) without first materializing an owned copy. The
allocating entry point allocates its result through a clone of `self`'s allocator.

Implementors: - `impl<Scalar: [Jaccards](#Jaccards), const MAX_RANK: usize, A: [TensorRef](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#TensorRef)<Scalar, MAX_RANK>> [JaccardsPackedOps](#JaccardsPackedOps)<Scalar, MAX_RANK> for A`

## jaccards_packed

Jaccard distances: C = self × packed_rightᵀ

```rust
fn jaccards_packed<PackedAlloc: Allocator>(
    &self,
    packed_right: &DotsPackedMatrix<Scalar, PackedAlloc>,
) -> Result<Tensor<Scalar::JaccardResult, Self::Alloc, MAX_RANK>, Error>
where
    Self::Alloc: Clone,
```

self must be 2D __[m,k]__ with contiguous rows. packed_right contains B __[n,k]__
packed. Returns C __[m,n]__, allocated like `self`.

Returns `Err` if:

- self is not 2D

- self has non-contiguous rows

- inner dimensions don't match

- output allocation fails

- the kernel refuses `packed_right`, like one packed under other capabilities

## jaccards_packed_into

Jaccard distances into an existing output, avoiding allocation.

```rust
fn jaccards_packed_into<PackedAlloc, OutputTensor, const OUTPUT_MAX_RANK: usize>(
    &self,
    packed_right: &DotsPackedMatrix<Scalar, PackedAlloc>,
    output: &mut OutputTensor,
) -> Result<(), Error>
where
    PackedAlloc: Allocator,
    OutputTensor: TensorMut<Scalar::JaccardResult, OUTPUT_MAX_RANK>,
```

The output may be a `&mut Tensor<...>` or `&mut TensorSpan<...>`; any writable tensor
container that implements [`TensorMut`](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#TensorMut) works. The kernel overwrites `c` entirely, so it
need not arrive pre-initialized.

## SymmetricHammingsOps

Extension trait: symmetric Hamming distance matrix for any [`TensorRef`](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#TensorRef) implementor.

```rust
pub trait SymmetricHammingsOps<Scalar: Hammings, const MAX_RANK: usize>: TensorRef<Scalar, MAX_RANK>
```

Blanket-implemented for every `TensorRef<Scalar, R>`, exposing [`hammings_symmetric`](#hammings_symmetric) on owned
[`Tensor`](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#Tensor) as well as borrowed views. The kernel writes only the upper triangle, including the
diagonal — the lower triangle is not touched, so mirror it if you need a fully-populated matrix.

Prefer this trait when writing generic code over [`TensorRef`](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#TensorRef); use the inherent
[`TensorView::hammings_symmetric`](#hammings_symmetric) when you already hold a view.

Implementors: - `impl<Scalar: [Hammings](#Hammings), const R: usize, OutputTensor: [TensorRef](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#TensorRef)<Scalar, R>> [SymmetricHammingsOps](#SymmetricHammingsOps)<Scalar, R> for OutputTensor`

## hammings_symmetric

Computes the upper triangle of the symmetric Hamming-distance matrix of the rows with themselves.

```rust
fn hammings_symmetric(&self) -> Result<Tensor<u32, Self::Alloc, MAX_RANK>, Error>
where
    Self::Alloc: Clone,
```

## hammings_symmetric_into

Writes the symmetric Hamming-distance matrix into pre-allocated output, touching only the upper triangle of it.

```rust
fn hammings_symmetric_into<Out, const OUTPUT_MAX_RANK: usize>(
    &self,
    output: &mut Out,
) -> Result<(), Error>
where
    Out: TensorMut<u32, OUTPUT_MAX_RANK>,
```

## SymmetricJaccardsOps

Extension trait: symmetric Jaccard distance matrix for any [`TensorRef`](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#TensorRef) implementor.

```rust
pub trait SymmetricJaccardsOps<Scalar: Jaccards, const MAX_RANK: usize>: TensorRef<Scalar, MAX_RANK>
```

Blanket-implemented for every `TensorRef<Scalar, R>`, so `vectors.jaccards_symmetric()` is
available on both owned [`Tensor`](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#Tensor) and borrowed views. The kernel writes only the upper
triangle, including the diagonal; mirror to the lower triangle yourself if a dense symmetric
result is required.

Prefer this trait when writing generic code over [`TensorRef`](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#TensorRef); use the inherent
[`TensorView::jaccards_symmetric`](#jaccards_symmetric) when you already hold a view.

Implementors: - `impl<Scalar: [Jaccards](#Jaccards), const R: usize, OutputTensor: [TensorRef](https://www.unum.cloud/docs/numkong/rust/tensors-and-views.md#TensorRef)<Scalar, R>> [SymmetricJaccardsOps](#SymmetricJaccardsOps)<Scalar, R> for OutputTensor`

## jaccards_symmetric

Computes the upper triangle of the symmetric Jaccard-distance matrix of the rows with themselves.

```rust
fn jaccards_symmetric(&self) -> Result<Tensor<Scalar::JaccardResult, Self::Alloc, MAX_RANK>, Error>
where
    Self::Alloc: Clone,
```

## jaccards_symmetric_into

Writes the symmetric Jaccard-distance matrix into pre-allocated output, touching only the upper triangle of it.

```rust
fn jaccards_symmetric_into<Out, const OUTPUT_MAX_RANK: usize>(
    &self,
    output: &mut Out,
) -> Result<(), Error>
where
    Out: TensorMut<Scalar::JaccardResult, OUTPUT_MAX_RANK>,
```

## Attention

Trait abstracting ragged-attention pack/compute operations per scalar type.

```rust
pub trait Attention: StorageElement + Clone
```

Errors: [`Error::KernelFailed`](https://www.unum.cloud/docs/numkong/rust/quickstart.md#KernelFailed) when the kernel refuses to run, like on a buffer packed under
other [`Capabilities`](https://www.unum.cloud/docs/numkong/rust/runtime-dispatch.md#Capabilities) than the current ones.

Implementors: - `impl [Attention](#Attention) for [bf16](https://www.unum.cloud/docs/numkong/rust/scalar-types.md#bf16)`
- `impl [Attention](#Attention) for [f16](https://www.unum.cloud/docs/numkong/rust/scalar-types.md#f16)`
- `impl [Attention](#Attention) for [e4m3](https://www.unum.cloud/docs/numkong/rust/scalar-types.md#e4m3)`
- `impl [Attention](#Attention) for i8`

## attention_pack_size

Returns bytes enough to pack `token_count` keys in `segment_count` segments, however split.

```rust
fn attention_pack_size(
    heads: usize,
    depth: usize,
    token_count: usize,
    segment_count: usize,
) -> Result<usize, Error>
```

## attention_packed_shape

Reads the KV-cache geometry — heads, depth, segments — from the packed header. # Safety [`packed`](https://www.unum.cloud/docs/numkong/rust/scalar-types.md#packed) must point to a buffer produced by [`attention_pack`](#attention_pack).

```rust
unsafe fn attention_packed_shape(packed: *const u8) -> Result<(usize, usize, usize), Error>
```

## attention_pack

Pack a window of the `(segment, kv_head)` task grid into the KV-cache blob. # Safety - `k` / `v` must point to token matrices with `key_stride` / `value_stride` byte rows covering every token addressed by `key_offsets` + `key_lengths` - `key_offsets` holds `segment_count + 1` non-decreasing slot boundaries, and `key_lengths` is null or holds one count per segment, at most its slot's width - `key_value_packed` must have at least `attention_pack_size(..)` bytes

```rust
unsafe fn attention_pack(
    heads: usize,
    depth: usize,
    key_offsets: *const u32,
    key_lengths: *const u32,
    segment_count: usize,
    keys: *const Self,
    key_stride: usize,
    values: *const Self,
    value_stride: usize,
    key_value_packed: *mut u8,
    tasks_begin: usize,
    tasks_end: usize,
) -> Result<(), Error>
```

Windows run in any order: the one starting at task 0 also writes the header and directory,
which no window reads.

## attention_packed

Compute attention over the half-open task window `tasks_begin..tasks_end` of the `(query token, head)` grid, task `token * head_count + head`, the end clipped to the grid.

```rust
unsafe fn attention_packed(
    head_count: usize,
    heads: usize,
    depth: usize,
    query_offsets: *const u32,
    query_token_count: usize,
    scale: f32,
    keys_before: usize,
    keys_after: usize,
    queries: *const Self,
    query_stride: usize,
    key_value_packed: *const u8,
    output: *mut f32,
    output_stride: usize,
    log_sum_exp: *mut f32,
    tasks_begin: usize,
    tasks_end: usize,
) -> Result<(), Error>
```

Each segment's queries align to the end of its keys: row `r` of a segment with `q` queries
and `k` keys sits at position `p = r + k - q` and sees key `j` when
`p - keys_before <= j <= p + keys_after`. `usize::MAX` leaves a side unbounded, so causal is
`(usize::MAX, 0)`, a sliding window of `w` keys is `(w - 1, 0)` and bidirectional is
`(usize::MAX, usize::MAX)`. Rows that see no key are written as zeros.

Safety: - `key_value_packed` must have been produced by [`attention_pack`](#attention_pack) with matching geometry

- `queries` rows addressed by `query_offsets` must be valid, `output` writable
with `output_stride` byte rows

- `query_token_count` equals `query_offsets[segments]`

- `log_sum_exp` is null or writable for one value per query token and head

## AttentionPackedMatrix

Pre-packed ragged KV-cache for scaled-dot-product attention.

```rust
pub struct AttentionPackedMatrix<Scalar: Attention, Alloc: Allocator = Global>
```

Owns the backend-opaque blob plus the geometry needed to validate query batches:
segment count, KV head count, head width, and total token count.

Implements: `Debug`, `Send`, `Sync`

## clone

Clone this packed KV-cache, returning an error on allocation failure.

```rust
pub fn clone(&self) -> Result<Self, Error>
```

## empty_in

An empty cache that owns no allocation, holding only the given allocator.

```rust
pub fn empty_in(alloc: Alloc) -> Self
```

Nothing is packed yet: every geometry field reads zero and [`as_bytes`](https://www.unum.cloud/docs/numkong/rust/maxsim.md#as_bytes) is
empty until the first [`pack_into`](https://www.unum.cloud/docs/numkong/rust/maxsim.md#pack_into), which allocates on demand and can then
be re-run each decode step to reuse the buffer.

## new_in

Pack `keys`/`values` into a freshly allocated cache using the given allocator.

```rust
pub fn new_in<KeysTensor, ValuesTensor, const MAX_RANK: usize>(
    keys: &KeysTensor,
    values: &ValuesTensor,
    depth: usize,
    key_offsets: &[u32],
    key_lengths: Option<&[u32]>,
    alloc: Alloc,
) -> Result<Self, Error>
where
    KeysTensor: TensorRef<Scalar, MAX_RANK> + ?Sized,
    ValuesTensor: TensorRef<Scalar, MAX_RANK> + ?Sized,
```

## pack_into

Repack `keys`/`values` into this cache's existing blob, reusing the allocation when the packed size fits [`capacity`](https://www.unum.cloud/docs/numkong/rust/maxsim.md#capacity) and reallocating through the stored allocator only when it must grow.

```rust
pub fn pack_into<KeysTensor, ValuesTensor, const MAX_RANK: usize>(
    &mut self,
    keys: &KeysTensor,
    values: &ValuesTensor,
    depth: usize,
    key_offsets: &[u32],
    key_lengths: Option<&[u32]>,
) -> Result<(), Error>
where
    KeysTensor: TensorRef<Scalar, MAX_RANK> + ?Sized,
    ValuesTensor: TensorRef<Scalar, MAX_RANK> + ?Sized,
```

The decode-loop path: allocate the cache once at layer-init, then refresh it every
forward step with no further allocation. Packing overwrites, so a grow discards the old
contents rather than copying them.

`key_offsets` holds the `segment_count + 1` boundaries of the segments' key slots, and
`key_lengths`, when given, how many keys each slot holds, at most its width; `None` fills
every slot.

## reserve

Pre-grow the cache to hold the given ragged geometry, so a later [`pack_into`](https://www.unum.cloud/docs/numkong/rust/maxsim.md#pack_into) that fits stays allocation-free with a stable pointer — allocate once at layer-init for the maximum sequence length, then refresh every decode step with no further allocation.

```rust
pub fn reserve(&mut self, heads: usize, depth: usize, key_lengths: &[u32]) -> Result<(), Error>
```

`key_lengths` carries one token count per segment.

## attention_into

Ragged attention into a caller-provided `f32` output tensor of the same logical shape as `q` — __[tokens,heads * depth]__, contiguous rows.

```rust
pub fn attention_into<QueriesTensor, OutTensor, const MAX_RANK: usize, const OUT_MAX_RANK: usize>(
    &self,
    queries: &QueriesTensor,
    query_offsets: &[u32],
    scale: Option<f32>,
    keys_before: usize,
    keys_after: usize,
    output: &mut OutTensor,
) -> Result<(), Error>
where
    QueriesTensor: TensorRef<Scalar, MAX_RANK> + ?Sized,
    OutTensor: TensorMut<f32, OUT_MAX_RANK> + ?Sized,
```

Each segment's queries align to the end of its keys, and query row `r` of a segment with
`q` queries and `k` keys sees key `j` when `r + k - q - keys_before <= j <= r + k - q + keys_after`. `usize::MAX` leaves a side unbounded: causal is `(usize::MAX, 0)`, a sliding
window of `w` keys is `(w - 1, 0)` and bidirectional is `(usize::MAX, usize::MAX)`. Rows
that see no key are written as zeros.

## pack_size

Bytes a packed cache needs for `token_count` keys in `segment_count` segments however they split, the size query mirroring [`Dots::dots_pack_size`](#dots_pack_size).

```rust
pub fn pack_size(
    heads: usize,
    depth: usize,
    token_count: usize,
    segment_count: usize,
) -> Result<usize, Error>
```

Useful for pre-sizing an external buffer before packing, without constructing a cache.

## peek_shape

Read the geometry of an externally-produced packed KV blob straight from its self-describing header, returning `(heads, depth, segments)`, or [`Error::InvalidShape`](https://www.unum.cloud/docs/numkong/rust/quickstart.md#InvalidShape) if the slice is too short to hold one.

```rust
pub fn peek_shape(packed: &[u8]) -> Result<(usize, usize, usize), Error>
```

Unlike the dots packed matrix, the attention blob records its own shape,
so no caller-supplied dimensions are needed.

Reads via the C `nk_attention_packed_shape_serial_<dtype>` accessor for this scalar type.

## from_packed_bytes_in

Adopt an externally-produced packed KV blob by copying `bytes` into a container-owned allocation.

```rust
pub unsafe fn from_packed_bytes_in(bytes: &[u8], alloc: Alloc) -> Result<Self, Error>
```

The attention blob is self-describing, so `(heads, depth, segments)` are read
back from its header; [`tokens`](#tokens) reports `0` for an adopted blob because the
total token count is not part of the queryable header.

Safety: `bytes` must be a valid attention packing for [`Scalar`](https://www.unum.cloud/docs/numkong/rust/elementwise-and-reductions.md#Scalar), produced by this build's packer;
anything else makes a later [`attention`](#attention) read out of bounds.

## shape

Returns the shape `(heads, depth, segments)` of the packed cache.

```rust
pub fn shape(&self) -> (usize, usize, usize)
```

## segments

Returns the number of ragged segments in the packed cache.

```rust
pub fn segments(&self) -> usize
```

## heads

Returns the number of KV heads per token.

```rust
pub fn heads(&self) -> usize
```

## depth

Returns the number of channels per head.

```rust
pub fn depth(&self) -> usize
```

## tokens

Returns the total KV token count across segments.

```rust
pub fn tokens(&self) -> usize
```

## capacity

Bytes currently allocated for the packed blob (>= the live packed size).

```rust
pub fn capacity(&self) -> usize
```

## new

Pack ragged K/V token matrices using the global allocator.

```rust
pub fn new<KeysTensor, ValuesTensor, const MAX_RANK: usize>(
    keys: &KeysTensor,
    values: &ValuesTensor,
    depth: usize,
    key_offsets: &[u32],
    key_lengths: Option<&[u32]>,
) -> Result<Self, Error>
where
    KeysTensor: TensorRef<Scalar, MAX_RANK> + ?Sized,
    ValuesTensor: TensorRef<Scalar, MAX_RANK> + ?Sized,
```

## attention

Ragged attention allocating a fresh `f32` output tensor through a clone of this cache's allocator.

```rust
pub fn attention<QueriesTensor, const MAX_RANK: usize>(
    &self,
    queries: &QueriesTensor,
    query_offsets: &[u32],
    scale: Option<f32>,
    keys_before: usize,
    keys_after: usize,
) -> Result<Tensor<f32, Alloc>, Error>
where
    QueriesTensor: TensorRef<Scalar, MAX_RANK> + ?Sized,
    Alloc: Clone,
```

The allocating twin of [`attention_into`](#attention_into).

## AttentionRope

In-place NeoX split-half RoPE over a row-major __[rows,head_count × depth]__ tensor.

```rust
pub trait AttentionRope: Sized + StorageElement
```

Implementors: - `impl [AttentionRope](#AttentionRope) for f32`
- `impl [AttentionRope](#AttentionRope) for [bf16](https://www.unum.cloud/docs/numkong/rust/scalar-types.md#bf16)`
- `impl [AttentionRope](#AttentionRope) for [e4m3](https://www.unum.cloud/docs/numkong/rust/scalar-types.md#e4m3)`

## attention_rope_into

Rotates a 2D __[rows,head_count × depth]__ tensor in place using the __[rows,depth/2]__ [`cos`](https://www.unum.cloud/docs/numkong/rust/elementwise-and-reductions.md#cos)/[`sin`](https://www.unum.cloud/docs/numkong/rust/elementwise-and-reductions.md#sin) angle grids, shared across heads, for an even [`depth`](#depth) of channels per head.

```rust
fn attention_rope_into<XMut, const RX: usize>(
    x: &mut XMut,
    cos: &[f32],
    sin: &[f32],
    head_count: usize,
    depth: usize,
) -> Result<(), Error>
where
    XMut: TensorMut<Self, RX> + ?Sized,
```

The row stride is read from the tensor, so `x` may be a non-contiguous sub-span, for example
the Q or K column-section of a fused QKV buffer. Returns `Err` on a shape mismatch.

