Mojo · Inputs and Outputs

Inputs and Outputs

from usearch import DenseTraversalStats, DenseInsertConfig, DenseSearchConfig, … 3.0.0-rc.1 · 4c3e5023

§ Arenas

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 thread
for 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 arenas
var 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 call
var 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.

§ Rows & Scalars

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 call
var 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:

var stats = DenseTraversalStats()
_ = index.on(executor).search(queries, 10, arena, DenseSearchConfig(expansion_factor=128, stats=Pointer(to=stats)))
print(stats.computed_distances, "distances computed")

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_membersc_size_tGraph members visited while resolving the batch.
computed_distancesc_size_tDistances computed while resolving the batch, diversity re-selections included.
restartsc_size_tRe-seedings after the frontier drained prematurely - a connectivity health signal.
missing_hintsc_size_tHint 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
struct DenseInsertConfig[stats_origin: MutOrigin = MutUntrackedOrigin]

Parameters

stats_originMutOrigincomptimeThe lent telemetry slot's origin, inferred where one is lent.

Fields

expansion_factorIntThe breadth of the candidates the build walk explores; zero derives.
statsOptional[Pointer[DenseTraversalStats, stats_origin]]Receives this call's traversal telemetry, fully overwritten; absent skips reporting.

Conforms to

Copyable, Deinitable, ImplicitlyCopyable, Movable
__init__Names only the knobs that differ from the derived ones.
def __init__(
    out self,
    *,
    expansion_factor: Int = Int(0),
    stats: Optional[Pointer[DenseTraversalStats, stats_origin]] = Optional()
)

Parameters

expansion_factorIntThe breadth of the candidates the build walk explores; zero derives.
statsOptional[Pointer[DenseTraversalStats, stats_origin]]Receives this call's traversal telemetry; absent skips reporting.
StructDenseSearchConfigThe knobs of usearch_dense_search_t; zeroed or absent ones derive what the C interface derives.1
struct DenseSearchConfig[stats_origin: MutOrigin = MutUntrackedOrigin]

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_originMutOrigincomptimeThe lent telemetry slot's origin, inferred where one is lent.

Fields

expansion_factorIntThe breadth of the candidates the walk explores; zero derives.
traversalTraversalA walk, or an exact scan of every entry on RAM and NVMe handles; VRAM answers UNSUPPORTED.
predicateAddressA usearch_predicate_callback_t that must outlive the call; NULL_ADDRESS admits all.
predicate_stateAddressThe opaque state forwarded into every predicate call.
statsOptional[Pointer[DenseTraversalStats, stats_origin]]Receives this call's traversal telemetry, fully overwritten; absent skips reporting.

Conforms to

Copyable, Deinitable, ImplicitlyCopyable, Movable
__init__Names only the knobs that differ from the derived ones.
def __init__(
    out self,
    *,
    expansion_factor: Int = Int(0),
    traversal: Traversal = Traversal.WALK,
    predicate: Int = Int(0),
    predicate_state: Int = Int(0),
    stats: Optional[Pointer[DenseTraversalStats, stats_origin]] = Optional()
)

Parameters

expansion_factorIntThe breadth of the candidates the walk explores; zero derives.
traversalTraversalA walk, or an exact scan of every entry on RAM and NVMe handles.
predicateIntA usearch_predicate_callback_t that must outlive the call; NULL_ADDRESS admits all.
predicate_stateIntThe opaque state forwarded into every predicate call.
statsOptional[Pointer[DenseTraversalStats, stats_origin]]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

codec_intThe raw usearch_scalar_t value the ABI exchanges.

Values

UNKNOWNScalarKind(Int32(0))No scalar named - an unset field, or a custom metric owning its own storage.
F64ScalarKind(Int32(1))IEEE-754 binary64.
F32ScalarKind(Int32(2))IEEE-754 binary32.
BF16ScalarKind(Int32(3))Truncated f32 - f32's range on a 7-bit mantissa.
F16ScalarKind(Int32(4))IEEE-754 binary16.
E5M2ScalarKind(Int32(5))FP8 with a 5-bit exponent - f16's range on two mantissa bits.
E4M3ScalarKind(Int32(6))FP8 with a 4-bit exponent, no infinities.
E3M2ScalarKind(Int32(7))FP6 with a 3-bit exponent, byte-aligned with two padding bits.
E2M3ScalarKind(Int32(8))FP6 with a 2-bit exponent, byte-aligned with two padding bits.
I8ScalarKind(Int32(10))8-bit signed integer.
I4X2ScalarKind(Int32(11))Two signed nibbles per byte, even dimensions in the low nibble.
I32ScalarKind(Int32(12))32-bit signed integer - the coordinates of geospatial points.
U8ScalarKind(Int32(20))8-bit unsigned integer.
U4X2ScalarKind(Int32(21))Two unsigned nibbles per byte, even dimensions in the low nibble.
U1X8ScalarKind(Int32(22))Eight single-bit dimensions per byte - what Hamming and Jaccard score.

Conforms to

Copyable, Deinitable, Equatable, ImplicitlyCopyable, Movable, RegisterPassable, TrivialRegisterPassable
ofThe kind a Mojo scalar type stores one-to-one; the packed and FP6 kinds have none. Binding-only.
def of[dtype: DType]() -> Self

Parameters

dtypeDTypecomptimeThe element type of the caller's storage.

Returns

The matching kind, or UNKNOWN, which every verb refuses.
bits_per_scalarHow many bits one logical dimension occupies, which sub-byte kinds share.
def bits_per_scalar(self) -> Int

Returns

The width in bits, or zero for UNKNOWN, whose storage the caller owns.
bytes_for_columnsThe natural width of one row, rounding a sub-byte packing up to whole bytes.
def bytes_for_columns(self, columns: Int) -> Int

Parameters

columnsIntLogical scalars in the row, as the caller counts them.

Returns

The byte count a contiguous row of that width occupies.
storesWhether rows of this kind read as whole dtype elements: equal widths, or bytes for packed kinds. Binding-only.
def stores[dtype: DType](self) -> Bool

Parameters

dtypeDTypecomptimeThe element type the caller reads the rows as.

Returns

True when one element of dtype spans exactly one scalar, or one byte of a packed kind.
StructTraversalHow a search reaches the indexed entries it scores - usearch_traversal_t.
struct Traversal

Fields

codec_intThe raw usearch_traversal_t value the ABI exchanges.

Values

WALKTraversal(Int32(1))Walk the graph, scoring the entries the expansion reaches - approximate, the default.
SCANTraversal(Int32(2))Score every entry published when the query's scan starts - exact, on RAM and NVMe handles.

Conforms to

Copyable, Deinitable, Equatable, ImplicitlyCopyable, Movable, RegisterPassable, TrivialRegisterPassable
StructRowsViewA borrowed run of rows of one width - usearch_rows_view_t, the batch currency every verb reads.2
struct RowsView[origin: ImmOrigin = ImmUntrackedOrigin]

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

originImmOrigincomptimeThe borrowed rows, named so they outlive every call this view crosses.

Fields

dataPointer[NoneType, ImmUntrackedOrigin]Base pointer of the first row; non-null, since a Mojo pointer cannot be otherwise.
rowsc_size_tRows the view carries, which the verb takes as its count.
columnsc_size_tLogical scalars per row, as the caller counts them.
stridec_size_tBytes between the starts of consecutive rows; zero repeats the first row.
scalarScalarKindThe scalar type the rows hold, converted on the way in where it differs.

Conforms to

Copyable, Deinitable, ImplicitlyCopyable, Movable, RegisterPassable, TrivialRegisterPassable
ofReads a flat run of scalars as contiguous rows of the given width. Binding-only.
def of[dtype: DType, rows_origin: ImmOrigin, //](
    rows: Span[Scalar[dtype], rows_origin],
    columns: Int,
    scalar: ScalarKind = ScalarKind.of[dtype]()
) -> RowsView[rows_origin]

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

dtypeDTypecomptimeThe scalar type of the run, inferred from the argument.
rows_originImmOrigincomptimeThe run's origin, inferred with it.
rowsSpan[Scalar[dtype], rows_origin]The scalars, laid out row after row with no padding between them.
columnsIntLogical scalars per row, which must divide the run into whole rows.
scalarScalarKindHow 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

originMutOrigincomptimeThe arena the run lives in, which this result borrows.

Fields

arenaPointer[Arena, origin]The arena holding the run.
generationIntThe arena's call this run came from, so a read after a later call is refused.
viewMatchesViewArgumentsWhere the engine wrote the keys, scores and counts, inside the arena.
queriesIntQueries the call answered.

Conforms to

Deinitable, Movable, Sized
__len__The queries the call answered.
def __len__(self) -> Int

Returns

One per query of that call.
query_keysOne query's keys, nearest first, trimmed to the matches that query found.
def query_keys(ref self, query: Int) -> Span[Key, origin_of(self)]

Parameters

queryIntThe query's offset in the batch, below len(self).

Returns

The keys, borrowed from the arena this result reads.

Raises

Error with UNINITIALIZED once a later call rewound the arena, or DEVICE over VRAM.
query_distancesOne query's scores, paired positionally with query_keys.
def query_distances(ref self, query: Int) -> Span[Float64, origin_of(self)]

Parameters

queryIntThe query's offset in the batch, below len(self).

Returns

The scores, borrowed from the arena this result reads.

Raises

Error with UNINITIALIZED once a later call rewound the arena, or DEVICE over VRAM.
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.
def __init__(out self, library: Library)
def __init__(out self, executor: Executor)

Memory where executor computes - host pages, or the memory of its GPU - serving the calls of its residency. Calls usearch_arena_init.

Parameters

libraryLibraryThe loaded engine, kept so the shared object stays mapped.
executorExecutorThe executor whose calls it serves; host pages may outlive it, and a VRAM arena keeps it alive.

Raises

Error if the engine cannot allocate the handle.
__deinit__Frees the arena and every output in it, then lets a VRAM arena's executor go. Calls usearch_arena_free.
def __deinit__(deinit self)

The executor and the loader reference are surrendered only after the call, for the reason _ExecutorHandle records.

residencyWhere its memory lives, decided once by the executor that made it. Binding-only.
def residency(self) -> Residency

Returns

VRAM for device memory, which host reads refuse, and the host residency otherwise.