# Metrics

> USearch for Zig

`const usearch = @import("usearch");`

`const DenseMetric = @import("usearch").DenseMetric;`

`const DenseIndex = @import("usearch").DenseIndex;`

## Distance

Distance or similarity score between two entries, in the metric of the index that produced it.

```zig
pub const Distance = f64
```

## DenseOptions

The geometry and metric an empty index freezes - what [`DenseIndex.initRam`](#initRam) and [`initVram`](#initVram) read beside their placement.

```zig
pub const DenseOptions = struct
```

Binding-only.

Stored vectors round up to whole steps of the distance kit the build picked, so the memory is
slightly larger than the logical vectors, and that is not a knob.

- `metric` `DenseMetric`: The metric kind used for distance calculations between vectors.
- `quantization` `Scalar`: The scalar type vectors are stored and quantized into.
- `dimensions` `usize`: Dimensions of the indexed vectors.
- `connectivity` `usize`: The optional connections-per-node bound; zero derives.
- `values_per_key` `usize`: Upper bound on entries sharing one key; zero is unbounded.

## DenseMetric

The dense metric kinds shipped out of the box, all derived from one inner-product kernel with pre-computed magnitudes.

```zig
pub const DenseMetric = enum(c_int)
```

The bit-level pair reads its rows as [`Scalar.u1x8`](https://www.unum.cloud/docs/usearch/zig/inputs-and-outputs.md#u1x8) and refuses any
other quantization; the first three refuse that one. Mirrors `usearch_dense_metric_t`.

Non-exhaustive, so a kind a newer engine reports arrives intact rather than coerced.

- `unknown` = `0`: No kind named - an unset field, refused by every constructor.
- `inner` = `1`: Sum of elementwise products, unnormalized - the only kind the linear-algebra verbs accept.
- `angular` = `2`: Cosine distance in [0, 2], blind to magnitude.
- `euclidean` = `3`: L2 distance, zero only for identical vectors.
- `hamming` = `4`: Count of differing bits, so its range is the dimensionality.
- `jaccard` = `5`: Differing bits over union bits in [0, 1], ignoring the zeros both vectors share.

## name

The canonical spelling, as `.inner` spells "inner".

```zig
pub fn name(self: DenseMetric) [:0]const u8
```

Calls `usearch_dense_metric_name`.

## fromName

Reads a spelling back, or null when no kind spells it.

```zig
pub fn fromName(text: []const u8) ?DenseMetric
```

Calls `usearch_dense_metric_from_name`.

## DenseIndex

Vector Search Index over equidimensional dense representations.

```zig
pub const DenseIndex = extern struct
```

A handle is shareable across threads: searches never write to the graph, and insertions
synchronize through per-node locks and publish in order. It is the [`Executor`](https://www.unum.cloud/docs/usearch/zig/executors.md#Executor) that orders,
never the index.

```
const usearch = @import("usearch");

var index = try usearch.DenseIndex.initRam(.{ .dimensions = 4, .metric = .angular, .quantization = .i8 }, &#x26;.{});
defer index.deinit();
try index.reserve(1_000);

const keys = [_]usearch.Key{ .fromInteger(0), .fromInteger(1) };
const slab = [_]f32{ 0.2, 0.1, 0.2, 0.1, 0.1, 0.2, 0.1, 0.2 };
try index.insert(&#x26;keys, try .of(f32, &#x26;slab, 4), .{});

const query = [_]f32{ 0.2, 0.1, 0.2, 0.1 };
var arena = try usearch.Arena.init(null);
defer arena.deinit();
const matches = try index.search(try .ofRow(f32, &#x26;query), 10, &#x26;arena, .{});
const nearest = (try matches.query(0)).keys[0];
```

- `handle` `?*anyopaque`: The opaque `usearch_dense_index_t` the ABI hands back.

## On

Every verb of the index, blocking on an executor after every call submitted there before it.

```zig
pub const On = Receiver(.blocking, void)
```

## DeferredOn

Every verb of the index, submitted to an executor into a `Deferred(Call)` the caller holds, borrowing the arena a verb lands in as a blocking call does; only the verb `Call` describes compiles.

```zig
pub fn DeferredOn(comptime Call: type) type
```

## on

Runs every verb on `executor`, whose kind must match the index's residency.

```zig
pub fn on(self: DenseIndex, executor: Executor) On
```

Binding-only.

## deferred

Submits a verb to `executor`, holding it in `call` - a `*Deferred(Call)` the caller places anywhere, which stays put until its [`deinit`](https://www.unum.cloud/docs/usearch/zig/retrieval-and-streaming.md#deinit).

```zig
pub fn deferred(
    self: DenseIndex,
    executor: Executor,
    call: anytype,
) DeferredOn(@FieldType(@TypeOf(call.*), "call"))
```

Binding-only.

## diagnosed

Runs every verb on the calling thread, copying each call's final outcome into `outcome`.

```zig
pub fn diagnosed(self: DenseIndex, outcome: *Outcome) On
```

Binding-only.

## initRam

Creates an empty, mutable index in host memory, one shard per memory domain; empty derives every node the process may allocate on.

```zig
pub fn initRam(
    options: DenseOptions,
    memory_domains: []const i32,
) Error!DenseIndex
```

Calls `usearch_dense_init_ram`.

## initVram

Creates an empty, mutable index in device memory, one shard per device and never a replica; empty derives one free device.

```zig
pub fn initVram(
    options: DenseOptions,
    devices: []const i32,
) Error!DenseIndex
```

Calls `usearch_dense_init_vram`.

## loadRam

Copies a checkpoint into a new, mutable index in host memory, shard by shard.

```zig
pub fn loadRam(
    path: []const u8,
    config: DenseLoadRamConfig,
) Error!DenseIndex
```

Point `path` at the `.safegraphs.json` manifest, never at one of the parts it enumerates.
The geometry and metric come from the checkpoint's own headers. Calls `usearch_dense_load_ram`.

## loadVram

Copies a checkpoint into a new, mutable index in device memory, with the placement rules of [`loadRam`](#loadRam).

```zig
pub fn loadVram(
    path: []const u8,
    config: DenseLoadVramConfig,
) Error!DenseIndex
```

Calls `usearch_dense_load_vram`.

## loadRamBuffer

Copies a monolith from a memory buffer into a new index in host memory, as [`loadRam`](#loadRam) does a path.

```zig
pub fn loadRamBuffer(
    buffer: []const u8,
    config: DenseLoadRamBufferConfig,
) Error!DenseIndex
```

Calls `usearch_dense_load_ram_buffer`.

## loadVramBuffer

Copies a monolith from a host or device buffer into a new index in device memory.

```zig
pub fn loadVramBuffer(
    buffer: []const u8,
    config: DenseLoadVramBufferConfig,
) Error!DenseIndex
```

Calls `usearch_dense_load_vram_buffer`.

## viewRam

Serves a checkpoint in place through a memory mapping, as a read-only index whose mutating verbs report [`Error.ReadOnly`](https://www.unum.cloud/docs/usearch/zig/errors.md#ReadOnly); the files must stay unmodified while it lives.

```zig
pub fn viewRam(path: []const u8) Error!DenseIndex
```

Calls `usearch_dense_view_ram`.

## viewNvme

Serves a checkpoint in place from NVMe drives, read-only and sized past RAM; until NVMe checkpoints carry keys it answers [`Error.Unsupported`](https://www.unum.cloud/docs/usearch/zig/errors.md#Unsupported).

```zig
pub fn viewNvme(
    path: []const u8,
    config: DenseViewNvmeConfig,
) Error!DenseIndex
```

Calls `usearch_dense_view_nvme`.

## metadataRead

Reads a checkpoint's geometry and metric, without loading it.

```zig
pub fn metadataRead(
    path: []const u8,
    config: DenseMetadataReadConfig,
) Error!DenseMetadata
```

Calls `usearch_dense_metadata_read`.

## metadataReadBuffer

Reads geometry and metric out of a serialized monolith buffer, without loading it.

```zig
pub fn metadataReadBuffer(
    buffer: []const u8,
    config: DenseMetadataReadBufferConfig,
) Error!DenseMetadata
```

Calls `usearch_dense_metadata_read_buffer`.

## deinit

Frees the resources associated with the index, waiting for every deferred call naming it; the handle is invalid afterwards.

```zig
pub fn deinit(self: *DenseIndex) void
```

Calls `usearch_dense_free`.

## reserve

Reserves room for a total number of entries, the current size included.

```zig
pub fn reserve(self: DenseIndex, capacity: usize) Error!void
```

Calls `usearch_dense_reserve`.

## clear

Erases all entries, keeping the reserved capacity and frozen geometry.

```zig
pub fn clear(self: DenseIndex) Error!void
```

Never runs
concurrently with any other verb on the same handle. Calls `usearch_dense_clear`.

## describe

Reports the frozen metadata and the current counters in one call.

```zig
pub fn describe(self: DenseIndex) Error!DenseIndexDescription
```

Calls `usearch_dense_describe`.

## count

The entries stored under `key`; zero means the index holds none.

```zig
pub fn count(self: DenseIndex, key: Key) Error!usize
```

Calls `usearch_dense_count`.

## contains

Whether the index holds at least one entry under `key`.

```zig
pub fn contains(self: DenseIndex, key: Key) Error!bool
```

Binding-only; calls `usearch_dense_count`.

## get

Every vector stored under `key` as `scalar`, one packed row per entry in insertion order, written in `arena`; an absent key yields no rows.

```zig
pub fn get(
    self: DenseIndex,
    key: Key,
    scalar: Scalar,
    arena: *Arena,
) Error!DenseVectors
```

Calls `usearch_dense_get`.

## insert

Adds a run of vectors under their keys - the native batch operation, one key per row.

```zig
pub fn insert(
    self: DenseIndex,
    keys: []const Key,
    vectors: RowsView,
    config: DenseInsertConfig,
) Error!void
```

The batch is all-or-nothing: every fallible step runs before the first entry identity is
claimed, so a handler reports progress but cannot stop it. Overlaps safely with concurrent
searches on the same handle, and the input scalar type is independent of the stored
quantization. Calls `usearch_dense_insert`.

## refine

Re-selects the neighbors of every stored entry against the finished graph, reporting the entries re-selected and changed and the islands healed; zero `changed` means settled.

```zig
pub fn refine(
    self: DenseIndex,
    config: DenseRefineConfig,
) Error!DenseRefineReport
```

A build step rather than a repair: a cheap insertion pass followed by one refinement
reaches a better graph, in less total time, than one expensive insertion pass - so a wide
expansion factor belongs here rather than in [`insert`](#insert). A stopped sweep leaves the graph
correct and merely less improved, and reports no [`Error.Cancelled`](https://www.unum.cloud/docs/usearch/zig/errors.md#Cancelled). Calls `usearch_dense_refine`.

## health

Audits how much of the graph a search reaches from its landmarks; the entries it misses stay unfound until a [`refine`](#refine) re-links them.

```zig
pub fn health(self: DenseIndex) Error!DenseReachability
```

Calls `usearch_dense_health`.

## search

Resolves a run of queries into up to `wanted` matches apiece, written in `arena` - the native batch operation.

```zig
pub fn search(
    self: DenseIndex,
    queries: RowsView,
    wanted: usize,
    arena: *Arena,
    config: DenseSearchConfig,
) Error!Matches
```

Never writes to the graph, so any number of these may overlap with concurrent insertions.
A zero `wanted` walks nothing and lands only the counts, every one zero.
Calls `usearch_dense_search`.

## absorb

Absorbs every donor into the index, one after another, keeping their graphs rather than re-inserting their vectors: each donor's entries append shard by shard, then a refinement links them across the appended part.

```zig
pub fn absorb(
    self: DenseIndex,
    donors: []DenseIndex,
    config: DenseAbsorbConfig,
) Error!DenseRefineReport
```

Donors must share the index's residency, scalar, metric and
geometry, or the call answers [`Error.ConfigMismatch`](https://www.unum.cloud/docs/usearch/zig/errors.md#ConfigMismatch).

Each donor it absorbs is consumed and nulled in place, so [`deinit`](https://www.unum.cloud/docs/usearch/zig/retrieval-and-streaming.md#deinit) on every donor afterwards
frees only those a stop between donors or a failure left live. Readers of a RAM index keep
searching throughout; a VRAM one admits no overlapping search. Calls `usearch_dense_absorb`.

## split

Splits the index into `receivers`, the inverse of [`absorb`](#absorb): graph-local parts keeping their edges, one per receiver, leaving this index empty but live.

```zig
pub fn split(
    self: DenseIndex,
    receivers: []const DenseIndex,
    config: DenseSplitConfig,
) Error!DenseRefineReport
```

Receivers must be empty and
share the index's residency, scalar, class, metric, geometry and shard count, or the call
answers [`Error.ConfigMismatch`](https://www.unum.cloud/docs/usearch/zig/errors.md#ConfigMismatch), and a refusal moves nothing. Calls `usearch_dense_split`.

## sample

Draws up to `wanted` keys around `config.focus`, or across the whole index, each hanging from a shown or earlier draw and placed in three dimensions beside the shown ones, written in `arena`; a larger `wanted` draws a superset.

```zig
pub fn sample(
    self: DenseIndex,
    wanted: usize,
    arena: *Arena,
    config: DenseSampleConfig,
) Error!DenseDraws
```

Only RAM samples yet. Calls `usearch_dense_sample`.

## cursorNext

Copies up to `wanted` more entries out of `cursor` into `arena` - their keys, and the keys each links to when [`links`](https://www.unum.cloud/docs/usearch/zig/retrieval-and-streaming.md#links) asks - answering an empty batch once the walk is exhausted.

```zig
pub fn cursorNext(
    self: DenseIndex,
    cursor: DenseCursor,
    wanted: usize,
    links: bool,
    arena: *Arena,
) Error!DenseCursorBatch
```

The
vectors themselves come from [`get`](#get). Calls `usearch_dense_cursor_next`.

## searchStreamNext

Pulls up to `wanted` more matches out of `stream` into `arena`, as one query's matches, answering an empty query once the walk is drained.

```zig
pub fn searchStreamNext(
    self: DenseIndex,
    stream: DenseSearchStream,
    wanted: usize,
    arena: *Arena,
) Error!Matches
```

Ordering beyond the configured expansion horizon is best-effort nearest rather than
globally exact, so pulling far past that breadth degrades the ranking rather than ending
the stream. Calls `usearch_dense_search_stream_next`.

## save

Saves the index under `path` in the layout its extension picks - a `.safegraphs` monolith, or a `.safegraphs.json` manifest beside its shards.

```zig
pub fn save(
    self: DenseIndex,
    path: []const u8,
    config: DenseSaveConfig,
) Error!void
```

Demands a mutation-free window; concurrent readers keep searching throughout. A failed
save leaves a file without its header, which every reader rejects, and that leftover is
the caller's to remove. Calls `usearch_dense_save`.

## saveBuffer

Saves the index as one monolith - the same bytes [`save`](#save) writes to a `.safegraphs` path - written in `arena`.

```zig
pub fn saveBuffer(
    self: DenseIndex,
    arena: *Arena,
    config: DenseSaveBufferConfig,
) Error!Monolith
```

Calls `usearch_dense_save_buffer`.

## multiply

Approximate sparse product of the left-hand rows against the indexed vectors, reporting every pair the traversal scored instead of only the nearest, as a ragged output written in `arena` with Apache Arrow offsets.

```zig
pub fn multiply(
    self: DenseIndex,
    left: RowsView,
    arena: *Arena,
    config: DenseMultiplyConfig,
) Error!DenseProducts
```

Pairs arrive in traversal order rather than sorted, and carry no duplicates. Cost is
roughly `rows × expansion × connectivity × dimensions`, sublinear in the indexed entries.
Runs on the inner-product kind only, refusing others with [`Error.ConfigMismatch`](https://www.unum.cloud/docs/usearch/zig/errors.md#ConfigMismatch).
Calls `usearch_dense_multiply`.

## attend

Softmax attention of a run of query heads over every indexed key their graph walks score, folded as Flash Attention folds key tiles - no top-k cut, so the long tail the walk scores brings the result toward exact dense attention.

```zig
pub fn attend(
    self: DenseIndex,
    queries: RowsView,
    value_lookup: ValueLookup,
    arena: *Arena,
    config: DenseAttendConfig,
) Error!DenseVectors
```

One walk per token serves its `config.head_count` heads, rows `t · head_count + h`. The
index stores no values: `value_lookup` resolves the keys of every scored batch to their
rows, a host function on RAM and a device one on VRAM. The attended rows land in `arena` as wide as the keys and divided by the partition,
and `carry` is worth asking for only to fold this branch into another over the same
queries. Runs on the inner-product kind only. Calls `usearch_dense_attend`.

## maxsim

Late-interaction scoring of multi-row queries against multi-vector documents: every query row takes its best match within a key, and those maxima sum to that key's score.

```zig
pub fn maxsim(
    self: DenseIndex,
    queries: RowsView,
    rows_per_query: usize,
    wanted: usize,
    arena: *Arena,
    config: DenseMaxsimConfig,
) Error!DenseProducts
```

`queries` carries the queries laid end to end, `rows_per_query` rows apiece and uniform, so
ColBERT's padding applies as is, and each query's keys land in `arena` largest first, up to
`wanted` of them, or every key it scored when `wanted` is zero. Approximate downward only,
and runs on the inner-product kind only. Calls `usearch_dense_maxsim`.

