# MaxSim

> NumKong for Rust

MaxSim is the late-interaction primitive used by systems such as [ColBERT](https://arxiv.org/abs/2004.12832).
It is not "just another matrix multiply".

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

let queries = Tensor::<f32>::full(&[4, 16], 1.0).unwrap();
let docs = Tensor::<f32>::full(&[8, 16], 1.0).unwrap();

let queries_packed = MaxSimPackedMatrix::new(&queries).unwrap();
let docs_packed = MaxSimPackedMatrix::new(&docs).unwrap();
let score = queries_packed.score(&docs_packed).unwrap();

assert!(score.is_finite());
```

`use numkong::MaxSim;`

`use numkong::MaxSimPackedMatrix;`

## MaxSim

Trait abstracting MaxSim pack/score operations per scalar type.

```rust
pub trait MaxSim: 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 [MaxSim](#MaxSim) for f32`
- `impl [MaxSim](#MaxSim) for [f16](https://www.unum.cloud/docs/numkong/rust/scalar-types.md#f16)`
- `impl [MaxSim](#MaxSim) for [bf16](https://www.unum.cloud/docs/numkong/rust/scalar-types.md#bf16)`

## Score

Score type returned by MaxSim scoring.

```rust
type Score: Clone + Default;
```

## maxsim_pack_size

Returns the packed buffer size in bytes for [`vectors`](#vectors) vectors of given [`depth`](https://www.unum.cloud/docs/numkong/rust/matrix-kernels.md#depth).

```rust
fn maxsim_pack_size(vectors: usize, depth: usize) -> Result<usize, Error>
```

## maxsim_packed_shape

Reads the packed vector count and depth from the buffer header. # Safety [`packed`](https://www.unum.cloud/docs/numkong/rust/scalar-types.md#packed) must point to a buffer produced by [`maxsim_pack`](#maxsim_pack).

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

## maxsim_pack

Pack vectors into backend-specific quantized format.

```rust
unsafe fn maxsim_pack(
    data: *const Self,
    vectors: usize,
    depth: usize,
    stride: usize,
    packed: *mut u8,
) -> Result<(), Error>
```

Packing fuses no transposition, so
depth-major vectors are transposed in a separate pass first.

Safety: - `data` must point to [`vectors`](#vectors) rows of [`depth`](https://www.unum.cloud/docs/numkong/rust/matrix-kernels.md#depth) elements, byte stride `stride`

- [`packed`](https://www.unum.cloud/docs/numkong/rust/scalar-types.md#packed) must have at least `maxsim_pack_size(vectors, depth)` bytes

## maxsim_packed

Compute MaxSim score on pre-packed buffers.

```rust
unsafe fn maxsim_packed(
    queries: *const u8,
    documents: *const u8,
    query_count: usize,
    document_count: usize,
    depth: usize,
    result: *mut Self::Score,
) -> Result<(), Error>
```

Safety: - Both buffers must have been produced by [`maxsim_pack`](#maxsim_pack) with matching depth

- `result` must point to valid, writable memory for [`Self::Score`](#Score)

## MaxSimPackedMatrix

Pre-packed vector set for MaxSim scoring.

```rust
pub struct MaxSimPackedMatrix<Scalar: MaxSim, Alloc: Allocator = Global>
```

Both query and document vectors must be packed before scoring. The buffer uses i8 quantization
for fast coarse screening, with full-precision originals retained for refinement.

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 set owning no allocation; fill it with [`pack_into`](#pack_into).

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

## new_in

Pack vectors from a 2D tensor view using a custom allocator.

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

Returns `Err` if the view is not 2D, the depth axis is not contiguous, the row stride is
negative, or allocation fails.

## pack_into

Repack `data` into this set's existing buffer, reusing the allocation when the packed size fits [`capacity`](#capacity) and reallocating through the stored allocator only when it must grow.

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

## reserve

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

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

## score

Compute the MaxSim score — sum over queries of the max cosine to any document vector — against another packed matrix, treating `self` as the queries and `other` as the documents.

```rust
pub fn score<OtherAlloc: Allocator>(
    &self,
    other: &MaxSimPackedMatrix<Scalar, OtherAlloc>,
) -> Result<Scalar::Score, Error>
```

Returns `Err` if:

- the two matrices were packed at different depths

- the kernel refuses a matrix, like one packed under other capabilities

## allocator

Returns a reference to the allocator.

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

## vectors

Number of vectors in the packed set.

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

## shape

Returns the shape __[vectors,depth]__ of the original vector set.

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

## pack_size

Bytes a packed buffer occupies for [`vectors`](#vectors) vectors of the given [`depth`](https://www.unum.cloud/docs/numkong/rust/matrix-kernels.md#depth) under the active backend's layout, letting a caller pre-size an external buffer without packing.

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

## from_packed_bytes_in

Adopt an externally-produced packed buffer by copying `bytes` into a container-owned allocation tagged with the given [`vectors`](#vectors) and [`depth`](https://www.unum.cloud/docs/numkong/rust/matrix-kernels.md#depth).

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

The packed layout is not
self-describing, so the caller must supply the shape the bytes were packed for.

Safety: `bytes` must be a valid packing of [`vectors`](#vectors) vectors of [`depth`](https://www.unum.cloud/docs/numkong/rust/matrix-kernels.md#depth) 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 [`score`](#score) read out of bounds.

## capacity

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

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

## clear

Reset to logically empty, keeping the allocation so the next [`pack_into`](#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 a 2D tensor of vectors for MaxSim scoring using the global allocator.

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

The [`MaxSimPackedMatrix::`](#MaxSimPackedMatrix) qualifier names the packing target — a tensor can be packed for
MaxSim or for dots, and those layouts differ, so construction goes through the typed
constructor rather than a bare `tensor.pack()`.

