C# · Metrics

Metrics

using Cloud.Unum.USearch; 3.0.0-rc.1 · 4c3e5023

§ Dense Indexes

DenseOptions is immutable, so a variant comes from DenseOptions.Default with { ... } rather than from mutating a value another handle may already have been built from.

API

ClassDenseIndexA Vector Search Index over equidimensional dense representations.33
DenseIndex.cs:314 ↗
class DenseIndex

Every verb is a batch over a RowsView, so a single vector is a one-row matrix. The breadth of the candidates rides each call rather than the handle, which lets a recall sweep run against one immutable index with no mutation between points.

The receiver chooses where a verb runs, and the verb stays the same: called on the index it runs on the caller's own thread, on On it blocks on that executor, and on Deferred it submits there and answers a Deferred<TResult>. The executor's kind must match the residency of the handle.

A handle may be shared across threads, and no method here takes a lock: every output whose length scales with the call lands in an Arena the caller lends. On a RAM handle, insertions and searches overlap freely: searches read only entries published behind the visibility watermark and never write to the graph, while insertions synchronize through per-node locks and publish in order. Serialization is the one exception, demanding a mutation-free window, though concurrent readers keep searching throughout.

InitRamCreates an empty, mutable index in host memory, one shard per named NUMA node.
DenseIndex.cs:330 ↗
DenseIndex InitRam(
    DenseOptions options,
    ReadOnlySpan<int> memoryDomains = default,
    CPUClass? cpuClass = null);

Calls usearch_dense_init_ram.

Parameters

optionsThe geometry and metric the index freezes.
memoryDomainsNUMA node IDs to place one shard on apiece; empty derives every node the process may allocate on.
cpuClassThe class the index dispatches as; null derives the CPU's own.

Returns

The new index.

InitVramCreates an empty, mutable index in device memory, one shard per named device, sharded and never replicated.
DenseIndex.cs:365 ↗
DenseIndex InitVram(
    DenseOptions options,
    ReadOnlySpan<int> devices = default,
    GPUClass? gpuClass = null);

Calls usearch_dense_init_vram.

Parameters

optionsThe geometry and metric the index freezes.
devicesCUDA device ordinals to place one shard on apiece; empty derives one free device.
gpuClassThe class the index dispatches as; null derives the devices' own.

Returns

The new index.

LoadRamCopies a checkpoint into a new, mutable index in host memory.
DenseIndex.cs:405 ↗
DenseIndex LoadRam(
    string path,
    ReadOnlySpan<int> memoryDomains = default,
    ulong valuesPerKey = 0,
    Executor? executor = null,
    CPUClass? cpuClass = null);

Point the path at a .safegraphs.json manifest or a single .safegraphs monolith; the geometry and metric come from the checkpoint's own headers. Calls usearch_dense_load_ram.

Parameters

pathThe checkpoint to read.
memoryDomainsWhere the parts land, part p on shard p modulo the shard count; empty derives every node.
valuesPerKeyThe upper bound on entries sharing one key, which no checkpoint records; zero is unbounded.
executorThe workers the read fans out over; null reads on the caller's thread.
cpuClassThe class the index dispatches as; null derives the CPU's own.

Returns

The new index.

LoadVramCopies a checkpoint into a new, mutable index in device memory, placing its parts as LoadRam does.
DenseIndex.cs:444 ↗
DenseIndex LoadVram(
    string path,
    ReadOnlySpan<int> devices = default,
    ulong valuesPerKey = 0,
    Executor? executor = null,
    GPUClass? gpuClass = null);

Calls usearch_dense_load_vram.

Parameters

pathThe checkpoint to read.
devicesWhere the parts land, part p on device p modulo their count; empty derives one free device.
valuesPerKeyThe upper bound on entries sharing one key; zero is unbounded.
executorThe workers the upload runs on; null uploads on the caller's thread.
gpuClassThe class the index dispatches as; null derives the devices' own.

Returns

The new index.

LoadRamBufferCopies a monolith from a buffer into a new, mutable index in host memory, as LoadRam does a path.
DenseIndex.cs:481 ↗
DenseIndex LoadRamBuffer(
    ReadOnlySpan<byte> buffer,
    ReadOnlySpan<int> memoryDomains = default,
    ulong valuesPerKey = 0,
    Executor? executor = null,
    CPUClass? cpuClass = null);

Calls usearch_dense_load_ram_buffer.

Parameters

bufferThe monolith's bytes, copied in before the call returns.
memoryDomainsThe NUMA nodes to place shards on; the monolith lands whole on the first.
valuesPerKeyThe upper bound on entries sharing one key; zero is unbounded.
executorThe workers the copy fans out over; null copies on the caller's thread.
cpuClassThe class the index dispatches as; null derives the CPU's own.

Returns

The new index.

LoadVramBufferCopies a monolith from a buffer into a new, mutable index in device memory.
DenseIndex.cs:518 ↗
DenseIndex LoadVramBuffer(
    ReadOnlySpan<byte> buffer,
    ReadOnlySpan<int> devices = default,
    ulong valuesPerKey = 0,
    Executor? executor = null,
    GPUClass? gpuClass = null);

Calls usearch_dense_load_vram_buffer.

Parameters

bufferThe monolith's bytes, in host or device memory.
devicesThe devices to place shards on; the monolith lands whole on the first.
valuesPerKeyThe upper bound on entries sharing one key; zero is unbounded.
executorThe workers the upload runs on; null uploads on the caller's thread.
gpuClassThe class the index dispatches as; null derives the devices' own.

Returns

The new index.

ViewRamServes a checkpoint in place through a memory mapping, as a read-only index whose mutating verbs report Status.ReadOnly; the files must stay unmodified while it lives.
DenseIndex.cs:553 ↗
DenseIndex ViewRam(string path);

Calls usearch_dense_view_ram.

Parameters

pathThe checkpoint to serve.

Returns

The read-only index.

ViewNvmeServes a checkpoint in place from NVMe drives, read through the executor's submission rings, as a read-only index.
DenseIndex.cs:583 ↗
DenseIndex ViewNvme(
    string path,
    ReadOnlySpan<string> paths = default,
    Executor? executor = null,
    CPUClass? cpuClass = null);

External-memory graphs are never modified in place, so every mutating verb fails on it with Status.ReadOnly. Calls usearch_dense_view_nvme.

Until NVMe checkpoints carry keys, the engine answers Status.Unsupported.

Parameters

pathThe checkpoint to serve.
pathsThe devices the parts are served from; empty serves each where its manifest points.
executorThe NVMe executor whose rings read the headers; null reads on the caller's thread.
cpuClassThe class the index dispatches as; null derives the CPU's own.

Returns

The read-only index.

MetadataReadReads a checkpoint's geometry and metric metadata without loading it - from the manifest's summary when the path names one, so no shard is opened.
DenseIndex.cs:624 ↗
DenseMetadata MetadataRead(string path, Executor? executor = null);

Calls usearch_dense_metadata_read.

Parameters

pathThe checkpoint path.
executorThe workers the read runs on; null reads on the caller's thread.

Returns

The geometry and metric the checkpoint's headers describe.

MetadataReadBufferReads geometry and metric metadata from a serialized monolith buffer.
DenseIndex.cs:649 ↗
DenseMetadata MetadataReadBuffer(
    ReadOnlySpan<byte> buffer,
    Executor? executor = null);

Calls usearch_dense_metadata_read_buffer.

Parameters

bufferThe monolith's bytes.
executorThe workers the read runs on; null reads on the caller's thread.

Returns

The geometry and metric the monolith's header describes.

OnThe receiver whose verbs block on executor , after every call submitted there before them.
DenseIndex.cs:672 ↗
DenseIndexOn On(Executor executor);

Binding-only.

Parameters

executorThe executor the verbs run on.

Returns

The receiver, holding nothing but the two references.

DeferredThe receiver whose verbs submit their calls to executor and answer a Deferred<TResult> at once.
DenseIndex.cs:678 ↗
DenseIndexDeferredOn Deferred(Executor executor);

Binding-only.

Parameters

executorThe executor the calls are submitted to.

Returns

The receiver, holding nothing but the two references.

ReserveClaims capacity for incoming vectors, counting the entries already stored - a hint, since insertions grow the index on demand.
DenseIndex.cs:683 ↗
void Reserve(ulong capacity);

Calls usearch_dense_reserve.

Parameters

capacityThe desired total capacity.
ClearErases all entries, keeping the reserved capacity and the frozen geometry; no other call on the handle may overlap it.
DenseIndex.cs:687 ↗
void Clear();

Calls usearch_dense_clear.

DescribeReports the frozen metadata and the current counters in one call.
DenseIndex.cs:692 ↗
DenseIndexDescription Describe();

Calls usearch_dense_describe.

Returns

The description.

CountCounts the entries stored under a key; zero means the index holds none.
DenseIndex.cs:698 ↗
ulong Count(Key key);

Calls usearch_dense_count.

Parameters

keyThe identity to look for.

Returns

How many entries share that key.

ContainsReports whether the index holds at least one entry under a key.
DenseIndex.cs:704 ↗
bool Contains(Key key);

Calls usearch_dense_count.

Parameters

keyThe identity to look for.

Returns

True when the index holds one or more.

GetRetrieves every vector stored under a key, one row per entry sharing the key, in insertion order, converted to the kind asked for and as wide as the library reports.
DenseIndex.cs:715 ↗
DenseVectors Get(
    Key key,
    Arena arena,
    Scalar outputsScalar = Scalar.F32);

Calls usearch_dense_get.

Parameters

keyThe identity to look for.
arenaWhere the rows land, rewound by the call.
outputsScalarThe scalar type to export as.

Returns

The rows, none when the key is absent, readable until the arena's next call.

InsertInserts a matrix of vectors under their keys - the native batch operation, which overlaps safely with concurrent searches on the same handle.
DenseIndex.cs:732 ↗
void Insert(
    ReadOnlyMemory<Key> keys,
    RowsView vectors,
    ulong expansionFactor = 0,
    DenseTraversalStats? stats = null);

Calls usearch_dense_insert.

The input scalar type is independent of the stored quantization; the library converts on ingest. A batch is all-or-nothing: every fallible step runs before the first entry identity is claimed, so a rejected batch publishes nothing.

Parameters

keysOne identity per row.
vectorsThe rows to admit.
expansionFactorThe breadth of the candidates the build walk explores; zero derives it from the geometry.
statsThe traversal telemetry to fill, when the caller wants any.
RefineRe-selects the neighbors of every stored entry against the finished graph, improving edges that were chosen when the graph around them was still incomplete.
DenseIndex.cs:753 ↗
RefineReport Refine(
    ulong expansionFactor = 0,
    DenseTraversalStats? stats = null);

Calls usearch_dense_refine.

This is a build step rather than a repair: a cheap insertion pass followed by one refinement reaches a better graph, in less total time, than a single expensive insertion pass - so a wide expansion factor belongs here rather than in Insert. Searches may run throughout, since edge re-selection publishes one neighbor list at a time and never removes a node.

Parameters

expansionFactorThe breadth of the candidates the re-selection walk explores; zero derives it from the geometry.
statsThe traversal telemetry to fill, when the caller wants any.

Returns

The nodes re-selected and changed, the islands found and reconnected, and the audit closing the heal; RefineReport.Changed at zero means the graph has settled, so a caller loops until it falls below whatever rate it considers settled.

HealthAudits how much of the graph a search can reach: forward reachability from as many landmarks as one search draws, swept level by level.
DenseIndex.cs:762 ↗
DenseReachability Health();

Read-only, so concurrent insertions merely leave fresh entries unaudited. Calls usearch_dense_health.

Returns

The population audited and how much of it is reachable.

MaxsimScores multi-row queries against multi-row entries by late interaction: every query row takes its best match within a key, and those maxima sum to that key's score.
DenseIndex.cs:815 ↗
DenseProducts Maxsim(
    RowsView queries,
    int rowsPerQuery,
    int wanted,
    Arena arena,
    ulong expansionFactor = 0,
    DenseTraversalStats? stats = null);

Calls usearch_dense_maxsim.

A document is one key holding its token rows, so this is what a values-per-key bound above one exists for. It runs on the inner-product kinds alone, since a maximum over logits is what late interaction sums, and a graph frozen under a distance kind refuses with Status.ConfigMismatch.

Parameters

queriesThe token rows of every query, laid end to end.
rowsPerQueryThe rows in every query; uniform, so the row count must be a whole multiple of it and the quotient is the query count.
wantedThe upper bound on documents per query - the "k" over documents rather than over rows; zero keeps every key the query scored.
arenaWhere the scored keys and their offsets land, rewound by the call.
expansionFactorThe breadth of the candidates the walk explores; zero derives it from the geometry.
statsThe traversal telemetry to fill, when the caller wants any.

Returns

The keys of every query, ragged, each key's summed maxima largest first.

MultiplyMultiplies a run of rows against the indexed vectors, reporting every pair the traversal scored rather than only the nearest - the approximate sparse product a graph walk reads off for free.
DenseIndex.cs:839 ↗
DenseProducts Multiply(
    RowsView left,
    Arena arena,
    ulong wanted = 0,
    ulong expansionFactor = 0,
    DenseTraversalStats? stats = null);

Calls usearch_dense_multiply.

Pairs arrive in traversal order, without duplicates, and the pairs the walk never reaches are the low and negative products. Runs on the inner-product kinds alone; a graph frozen under a distance kind refuses with Status.ConfigMismatch.

Parameters

leftThe left-hand rows, one walk apiece.
arenaWhere the pairs and their offsets land, rewound by the call.
wantedThe bound on pairs kept per row, the first ones its walk scored; zero keeps every pair.
expansionFactorThe breadth of the candidates, which widens the emitted pattern; zero derives it from the geometry.
statsThe traversal telemetry to fill, when the caller wants any.

Returns

The pairs of every left row, readable until the arena's next call.

AttendSoftmax attention of a run of query heads over every indexed key their graph walks score, folded as Flash Attention folds key tiles, with no top-k cut.
DenseIndex.cs:874 ↗
DenseVectors Attend(
    RowsView queries,
    RowsView values,
    Func<Key, int> rowOf,
    Arena arena,
    ulong headCount = 0,
    Memory<DenseSoftmaxState> carry = default,
    Scalar outputsScalar = Scalar.Unknown,
    ulong expansionFactor = 0,
    float scoreScale = 0,
    DenseTraversalStats? stats = null);

Calls usearch_dense_attend.

The index stores no values, so every scored key resolves to a row of values through rowOf , which runs on the executor's threads together. A key resolving outside it, or a values matrix of another scalar kind or width than the index's own, refuses the call with Status.ShapeMismatch, since a missing row is a wrong softmax rather than a smaller one. Runs on the inner-product kinds alone; a graph frozen under a distance kind refuses with Status.ConfigMismatch. rowOf is a host function, so it serves RAM handles alone: a VRAM walk resolves rows through a device function, which this binding does not build, so an attention into a Residency.Vram arena refuses with Status.Device.

Parameters

queriesThe query rows; row t * headCount + h is head h of token t.
valuesThe value rows the scored keys resolve into, pinned for the call.
rowOfResolves a key to its row of values .
arenaWhere the attended rows land, already divided by the partition, rewound by the call.
headCountThe query heads sharing one walk per token; it must divide the query count, and zero means one.
carryThe online-softmax state, one per query row, which merges this branch with another over the same queries; empty skips it.
outputsScalarThe scalar type the attended rows land as; Scalar.Unknown takes the queries' own.
expansionFactorThe breadth of the candidates; zero derives it from the geometry.
scoreScaleThe logit scale; zero derives the inverse square root of the dimensionality.
statsThe traversal telemetry to fill, when the caller wants any.

Returns

The attended rows, one per query row, readable until the arena's next call.

AbsorbAbsorbs every donor into this index, one after another, keeping their graphs rather than re-inserting their vectors and linking each in across the appended part.
DenseIndex.cs:895 ↗
RefineReport Absorb(params DenseIndex[] donors);

Calls usearch_dense_absorb.

An absorbed donor is consumed and refuses every later verb with Status.Uninitialized; one the call did not absorb, such as a donor of another residency, scalar, metric or geometry, which refuses with Status.ConfigMismatch, stays a live index the caller still owns, though its entries may already have moved here.

Parameters

donorsThe indexes to absorb.

Returns

What the closing refinement did to the graph.

SplitSplits this index into the empty receivers, the inverse of Absorb: the entries are partitioned along the graph, each part lands in one receiver with its edges kept, and a closing refinement re-links every receiver, leaving this index empty and live.
DenseIndex.cs:910 ↗
RefineReport Split(
    DenseIndex[] receivers,
    ulong seed = 0,
    ulong expansionFactor = 0,
    DenseTraversalStats? stats = null);

Calls usearch_dense_split.

Parameters

receiversAt least two empty indexes of this one's residency, scalar, metric, geometry and shard count.
seedPicks the part roots: equal seeds over an unchanged index split equally.
expansionFactorThe breadth of the closing walks; zero derives it.
statsThe traversal telemetry to fill, when the caller wants any.

Returns

What the closing refinements did, across the receivers.

SampleDraws keys around the focus, or across the whole index, each hanging from a shown or earlier draw and placed in three dimensions beside the shown ones - so a view grows one connected sample as it zooms.
DenseIndex.cs:924 ↗
Sample Sample(
    int wanted,
    Arena arena,
    DenseSampleConfig config = default);

A larger wanted under the same seed draws a superset; only a RAM index samples yet. Calls usearch_dense_sample.

Parameters

wantedThe upper bound on draws.
arenaWhere the draws are written, rewound by the call.
configThe keys shown and their coordinates, the focus, the seed and the walk.

Returns

The draws, readable until the arena's next call.

SaveWrites the index under a path, in the layout its extension picks - one .safegraphs monolith, or a .safegraphs.json manifest beside its shards.
DenseIndex.cs:933 ↗
void Save(string path);

Demands a mutation-free window; concurrent readers keep searching throughout. Calls usearch_dense_save.

Parameters

pathWhere the checkpoint is written.
SaveBufferWrites the index as one monolith, the same bytes Save writes to a .safegraphs path.
DenseIndex.cs:939 ↗
Monolith SaveBuffer(Arena arena);

Calls usearch_dense_save_buffer.

Parameters

arenaWhere the bytes land, rewound by the call.

Returns

The monolith, readable until the arena's next call.

CursorOpens a walk over the current contents, pulled on the caller's thread; each shard's publication watermark is read as the walk enters it, so an entry once passed is never revisited.
DenseIndex.cs:949 ↗
DenseCursor Cursor(ReadOnlySpan<Key> starts = default);

Without starts it yields every entry as stored; with them, the start keys' own entries first in order, then the entries those link to breadth-first, each once. Calls usearch_dense_cursor_init.

Parameters

startsThe keys to walk from; empty walks every row.

Returns

The cursor, which keeps the index alive until disposed.

SearchStreamOpens a resumable nearest-neighbor stream for one query, pulled on the caller's thread.
DenseIndex.cs:959 ↗
DenseSearchStream SearchStream(
    RowsView query,
    ulong expansionFactor = 0);

The stream keeps walk state rather than a thread. Calls usearch_dense_search_stream_init.

Parameters

queryThe single query row, whose row count must be one.
expansionFactorThe breadth of the candidates the walk explores; zero derives it from the geometry.

Returns

The stream, which keeps the index alive until disposed.

DisposeReleases the index, once every deferred call naming it is final.
DenseIndex.cs:964 ↗
void Dispose();

Freeing cannot fail, and a freed handle must not be used. Calls usearch_dense_free.