Every output whose length only the call knows, such as matches, rows, products, monoliths and cursor or stream batches, lands in an Arena you pass, and the verb answers a view over it without copying. Arena(executor) places it where that executor computes, decided once, and Arena(library) makes host pages for the calling thread.
Each call rewinds its arena, so the previous call’s views die and a steady loop allocates once.
To keep a result past the next call, or to read one while making the next, pass that call another arena.
An arena serves one call at a time, so each deferred call needs its own.
A view’s origin keeps its arena alive, and reading a stale one raises Status.UNINITIALIZED.
An arena from a VRAM executor holds device memory, which host accessors refuse with Status.DEVICE, and serves only that executor’s calls, keeping the executor alive until the arena goes.
var arena = Arena(executor) # Arena(library) makes host pages for the calling threadfor position in range(len(batches)): var matches = index.on(executor).search(batches[position], 10, arena) # rewinds: the last batch's matches are gone total += matches.query_distances(0)[0]var found = Arena(executor)var fetched = Arena(executor) # two live results, two arenasvar hits = index.on(executor).search(query, 10, found)for key in hits.query_keys(0): rerank(key, index.on(executor).get[DType.float32](key, fetched))var east_executor = Executor.for_ram(library, threads=4)var west_executor = Executor.for_ram(library, threads=4) # one executor and one arena per submitted callvar east_arena = Arena(east_executor)var west_arena = Arena(west_executor)var east = east_shard.deferred(east_executor).search(queries, 10, east_arena)var west = west_shard.deferred(west_executor).search(queries, 10, west_arena)print(east.wait().query_keys(0)[0], west.wait().query_keys(0)[0])
The arena is the verb’s last required argument, before the optional config, on every receiver.
A call that finds its arena serving another call finishes with Status.CONTENDED and rewinds nothing, so the holder’s views stay readable.
A call that fails for any other reason still rewinds it.
Every verb is a batch over strided rows, so a memory-mapped dataset reaches the index with no reshape and no copy, and a single vector is a one-row view.
A RowsView borrows a caller-owned run as rows of equal width, and it is the shape every batch argument takes. RowsView.of derives the row count and the byte pitch from a packed run, and the scalar kind from its element type.
var vectors = RowsView.of(embeddings, 768) # Float32 elements, so ScalarKind.F32
The library converts on ingest and on export, so the scalar kind of the input is independent of the index’s own quantization, and a Float32 query searches an I8 index without a conversion pass of your own.
Sub-byte kinds count logical dimensions rather than bytes and name their kind explicitly, so 128 dimensions of U1X8 occupy sixteen bytes per row:
var packed = List[UInt8](unsafe_uninit_length=rows * 16) # filled before the callvar bits = RowsView.of(packed, 128, ScalarKind.U1X8)
A dataset whose rows are padded to a wider pitch builds the view field by field, with its own stride in bytes.
Every verb with optional knobs takes a config named after its C argument structure, such as DenseInsertConfig, DenseSearchConfig or DenseMultiplyConfig, where every field left out derives what the C interface derives.
So the bare call takes none, and naming a field reaches a knob:
A stats slot is overwritten whole by every call it is lent to, so a reused one reads the last call’s figures rather than a running total.
A search answers Matches: a queries-by-wanted grid of keys, the distances paired with them, and one match count per query, all written into the arena. query_keys and query_distances borrow one query’s results, nearest first, trimmed to the count that query reported:
var matches = index.search(queries, 10, arena)for query in range(len(matches)): for rank in range(len(matches.query_keys(query))): print(query, matches.query_keys(query)[rank], matches.query_distances(query)[rank])
The breadth of the candidates rides each call as expansion_factor rather than the handle, so a recall sweep runs against one immutable index with no mutation between points. DenseSearchConfig(traversal=Traversal.SCAN) scores every entry instead of walking, on RAM and NVMe handles, which gives that sweep its exact ground truth.
API
StructDenseTraversalStatsTraversal telemetry - usearch_traversal_stats_t, a pure output.1
struct DenseTraversalStats
Fully overwritten by the call it is passed to, so a caller reusing one slot across calls reads
the last call's figures rather than a running total.
Fields
visited_members
c_size_t
Graph members visited while resolving the batch.
computed_distances
c_size_t
Distances computed while resolving the batch, diversity re-selections included.
restarts
c_size_t
Re-seedings after the frontier drained prematurely - a connectivity health signal.
missing_hints
c_size_t
Hint keys no shard held an entry under; zero unless a search was handed hints.
Conforms to
Copyable, Deinitable, ImplicitlyCopyable, Movable
__init__Zeroes every counter, so an unwritten slot reads as an untaken walk.
def __init__(out self)
StructDenseInsertConfigThe knobs of usearch_dense_insert_t; zeroed or absent ones derive what the C interface derives.1
A predicate narrows the matches to the entries it admits: the address of a
usearch_predicate_callback_t compiled in C, which installs unchanged. Non-matching entries still
route the traversal, so the predicate judges every scored candidate, a quad of them per call,
concurrently from the executor's threads.
Parameters
stats_originMutOrigin
comptime
The lent telemetry slot's origin, inferred where one is lent.
Fields
expansion_factor
Int
The breadth of the candidates the walk explores; zero derives.
Receives this call's traversal telemetry; absent skips reporting.
StructScalarKindThe scalar types the indexes store and exchange - usearch_scalar_t, named by width.4
struct ScalarKind
Ordered by shrinking dynamic range, floats first, then signed and unsigned integers. The xN
suffixes spell how many logical dimensions share one byte. Library.scalar_name spells each one
as the engine does.
Fields
code
c_int
The raw usearch_scalar_t value the ABI exchanges.
Values
UNKNOWN
ScalarKind(Int32(0))
No scalar named - an unset field, or a custom metric owning its own storage.
F64
ScalarKind(Int32(1))
IEEE-754 binary64.
F32
ScalarKind(Int32(2))
IEEE-754 binary32.
BF16
ScalarKind(Int32(3))
Truncated f32 - f32's range on a 7-bit mantissa.
F16
ScalarKind(Int32(4))
IEEE-754 binary16.
E5M2
ScalarKind(Int32(5))
FP8 with a 5-bit exponent - f16's range on two mantissa bits.
E4M3
ScalarKind(Int32(6))
FP8 with a 4-bit exponent, no infinities.
E3M2
ScalarKind(Int32(7))
FP6 with a 3-bit exponent, byte-aligned with two padding bits.
E2M3
ScalarKind(Int32(8))
FP6 with a 2-bit exponent, byte-aligned with two padding bits.
I8
ScalarKind(Int32(10))
8-bit signed integer.
I4X2
ScalarKind(Int32(11))
Two signed nibbles per byte, even dimensions in the low nibble.
I32
ScalarKind(Int32(12))
32-bit signed integer - the coordinates of geospatial points.
U8
ScalarKind(Int32(20))
8-bit unsigned integer.
U4X2
ScalarKind(Int32(21))
Two unsigned nibbles per byte, even dimensions in the low nibble.
U1X8
ScalarKind(Int32(22))
Eight single-bit dimensions per byte - what Hamming and Jaccard score.
One vector is a one-row view. The row count becomes the verb's count, and the column count
crosses with the rows, so the engine checks it against the geometry the index froze. The row
and column counts are scalars and stride alone is in bytes; a zero stride repeats the first
row for every one of them, as BLAS reads it.
Parameters
originImmOrigin
comptime
The borrowed rows, named so they outlive every call this view crosses.
Fields
data
Pointer[NoneType, ImmUntrackedOrigin]
Base pointer of the first row; non-null, since a Mojo pointer cannot be otherwise.
rows
c_size_t
Rows the view carries, which the verb takes as its count.
columns
c_size_t
Logical scalars per row, as the caller counts them.
stride
c_size_t
Bytes between the starts of consecutive rows; zero repeats the first row.
The row count comes from the bytes the run spans divided by the bytes one row of
scalar occupies, so a kind that disagrees with dtype is refused here rather than
read past the end by the engine. A sub-byte kind counts logical dimensions, so eight
U1X8 columns are one UInt8.
Parameters
dtypeDType
comptime
The scalar type of the run, inferred from the argument.
rows_originImmOrigin
comptime
The run's origin, inferred with it.
rowsSpan[Scalar[dtype], rows_origin]
The scalars, laid out row after row with no padding between them.
columnsInt
Logical scalars per row, which must divide the run into whole rows.
How the engine should read those scalars; the kind dtype stores by default.
Returns
A view of the whole run, strided at its natural row width.
Raises
Error when the run does not divide into whole rows of that width and kind.
argumentsThe batch-axis form the verbs take, its row count moved into the call's count.
def arguments(self) -> RowsViewArguments
Returns
The same base, stride, width, and scalar kind.
StructMatchesA ranked run a call wrote into its arena - usearch_matches_view_t, read in place.3
struct Matches[origin: MutOrigin]
Every ranking verb - the search, late interaction, and a stream pull - answers exactly this. Each
count is the number of matches written for its query, never more than wanted.
Parameters
originMutOrigin
comptime
The arena the run lives in, which this result borrows.
StructArenaThe memory every output whose length scales with a call lands in - usearch_arena_t, freed when it goes out of scope.3
struct Arena
Made where an executor computes, host pages or the memory of its GPU, decided once; nothing is
allocated until a call writes into it. Every call handed it rewinds and refills it, which
invalidates the previous call's outputs, so a steady loop allocates once. One arena serves one
call at a time; a second concurrent use is refused by the engine as CONTENDED, which leaves
the holder's results readable.
Every verb takes it as its last required argument, deferred ones too, and the result borrows it
in turn, so the arena outlives both; a failed call leaves it valid.
A result refuses every read with UNINITIALIZED once a later call rewinds its arena, and a
result over a VRAM arena refuses every host read with DEVICE. A VRAM arena serves only calls on
the executor that made it, and keeps that executor alive until it is freed.
Conforms to
Deinitable, Movable
__init__Host pages for calls on the calling thread. Calls usearch_arena_init.