JavaScript · Metrics

Metrics

import * as usearch from 'usearch';import { DenseIndex } from 'usearch'; 3.0.0-rc.1 · 4c3e5023

§ Dense Indexes

A dense index holds equidimensional embeddings. Only dimensions is required, and every other field derives from the geometry when it is left out:

const index = usearch.DenseIndex.initRam(
  {
    dimensions: 128,
    metric: usearch.DenseMetric.Inner, // or Angular, the default, Euclidean, Hamming, Jaccard
    quantization: usearch.Scalar.BF16, // the default, or F16, E5M2, E4M3, E3M2, E2M3, I8, I4x2, U8, U4x2, U1x8
    connectivity: 32, // a power of two; absent derives it from the geometry
    valuesPerKey: 2, // absent is unbounded
  },
  [0, 1], // absent spreads the shards over every NUMA node
);

API

Types

DenseMetricEnumeration representing the metric kinds used to score the dissimilarity of two vectors.
usearch.ts:55 ↗
enum DenseMetric

Values

Inner"inner"The inner product.
Angular"angular"The cosine distance.
Euclidean"euclidean"The L2 distance.
Hamming"hamming"The count of differing bits.
Jaccard"jaccard"The bit-level Jaccard distance.
DenseOptionsThe geometry every dense index freezes at initialization, whatever its residency.
usearch.ts:330 ↗
interface DenseOptions

Fields

dimensionsnumberScalars per row, fixed for the life of the index.
metric?DenseMetricHow dissimilarity is scored; defaults to angular.
quantization?ScalarThe scalar kind vectors are stored in; defaults to bf16.
connectivity?numberNeighbors per node; absent derives it from the geometry.
valuesPerKey?numberEntries one key may carry; absent is unbounded.
cpuClass?stringThe CPU class a host index dispatches as by its canonical name; absent dispatches as the machine.
gpuClass?stringThe GPU class a device index dispatches as by its canonical name; absent dispatches as the devices.
ClassDenseIndexA dense vector index, running every verb on the calling thread.34
dense_index.ts:1065 ↗
class DenseIndex extends DenseIndexOn implements Disposable

on(executor) and deferred(executor) reach the same verbs on an executor, all but attend, whose rowOf runs here.

initRamCreates an empty, mutable index in host memory, one shard per named NUMA node.
dense_index.ts:1158 ↗
static initRam(
    options: DenseOptions,
    memoryDomains?: number[],
): DenseIndex

Parameters

optionsDenseOptionsThe geometry.
memoryDomainsnumber[]NUMA node IDs to shard over; absent spreads the shards over every node.

Returns

An index a RAM executor's verbs run on.

Throws

Will throw an error if any option is of incorrect type or invalid value.

initVramCreates an empty, mutable index in device memory, one shard per named CUDA ordinal.
dense_index.ts:1181 ↗
static initVram(
    options: DenseOptions,
    devices?: number[],
): DenseIndex

Parameters

optionsDenseOptionsThe geometry.
devicesnumber[]CUDA ordinals to shard over; absent takes one free device.

Returns

An index a VRAM executor's verbs run on.

Throws

Will throw an error if any option is of incorrect type or invalid value.

loadRamCopies a checkpoint into a new, mutable index in host memory, part p loading into shard p modulo the shard count.
dense_index.ts:1204 ↗
static loadRam(
    path: string,
    config: DenseLoadRamConfig = {},
): DenseIndex

The geometry and metric come from the checkpoint itself.

Parameters

pathstringThe manifest of a sharded checkpoint, or a single monolith.
configDenseLoadRamConfigThe executor, the memory domains, and valuesPerKey.

Returns

The loaded index.
loadVramCopies a checkpoint into a new, mutable index in device memory, with the placement rules of DenseIndex.loadRam.
dense_index.ts:1219 ↗
static loadVram(
    path: string,
    config: DenseLoadVramConfig = {},
): DenseIndex

Parameters

pathstringThe manifest of a sharded checkpoint, or a single monolith.
configDenseLoadVramConfigThe executor, the devices, and valuesPerKey.

Returns

The loaded index.
loadRamBufferCopies a monolith held in memory into a new, mutable index in host memory.
dense_index.ts:1233 ↗
static loadRamBuffer(
    buffer: Uint8Array | Monolith,
    config: DenseLoadRamBufferConfig = {},
): DenseIndex

Parameters

bufferUint8Array | MonolithThe bytes DenseVerbs.saveBuffer produced.
configDenseLoadRamBufferConfigThe executor, the memory domains, and valuesPerKey.

Returns

The loaded index.
loadVramBufferCopies a monolith held in memory into a new, mutable index in device memory.
dense_index.ts:1254 ↗
static loadVramBuffer(
    buffer: Uint8Array | Monolith,
    config: DenseLoadVramBufferConfig = {},
): DenseIndex

Parameters

bufferUint8Array | MonolithThe bytes DenseVerbs.saveBuffer produced.
configDenseLoadVramBufferConfigThe executor, the devices, and valuesPerKey.

Returns

The loaded index.
viewRamServes a checkpoint in place through a memory mapping, as a read-only index whose mutating verbs throw with Status.ReadOnly; the files must stay unmodified while it lives.
dense_index.ts:1275 ↗
static viewRam(path: string): DenseIndex

Parameters

pathstringThe manifest of a sharded checkpoint, or a single monolith.

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.
dense_index.ts:1287 ↗
static viewNvme(
    path: string,
    config: DenseViewNvmeConfig = {},
): DenseIndex

Parameters

pathstringThe manifest of a sharded checkpoint, or a single monolith.
configDenseViewNvmeConfigThe NVMe executor, and the paths to serve the parts from.

Returns

The read-only index.
onThe same verbs, each blocking on executor, after every earlier submission to it.
dense_index.ts:1080 ↗
on(executor: Executor): DenseIndexOn

Binding-only.

Parameters

executorExecutorThe workers to run on; its residency must match the index's.
deferredThe same verbs, each submitted to executor and answered as a Deferred.
dense_index.ts:1088 ↗
deferred(executor: Executor): DenseIndexDeferredOn

Binding-only.

Parameters

executorExecutorThe workers to run on; its residency must match the index's.
attendSoftmax attention of every query row over every key its graph walk scores, folded as Flash Attention folds key tiles, writing one attended row per query in arena.
dense_index.ts:1116 ↗
attend(
    queries: RowsView | Vector,
    values: RowsView | Vector,
    rowOf: ValueRowOf,
    arena: Arena,
    config: DenseAttendConfig = {},
): DenseVectors

The index stores no values: every key a walk scores resolves through rowOf to a row of values, borrowed for the call, so only the keys a walk scores are ever resolved. The values must be as wide as the keys and stored in the index's own scalar kind, and a key resolving outside them refuses the call with ShapeMismatch, since a missing row is a wrong softmax rather than a smaller one. Row t * headCount + h of the queries, outputs and carry is head h of token t, every head of a token sharing one walk.

rowOf runs on the calling thread, which no executor's thread is, so only the index itself offers attend. The first exception it throws stops every later lookup and is rethrown once the call returns. Being a host function, it serves RAM handles alone: a VRAM walk resolves rows through a device function, which this binding does not build, so a Vram arena refuses with Status.Device.

Parameters

queriesRowsView | VectorOne query row, or a run of them, at the index's own width.
valuesRowsView | VectorThe value rows the scored keys resolve into, never copied.
rowOfValueRowOfAnswers one 16-byte key with the index of its row of values.
arenaArenaThe memory the attended rows land in, rewound by the call.
configDenseAttendConfigHead count, output kind, softmax shape, the carry, and the telemetry sink.

Returns

One key-width row per query, viewed inside arena.
freeReleases the handle now, waiting for every deferred call naming it; a freed index refuses every later call, and a second free does nothing.
dense_index.ts:1142 ↗
free(): void
metadataReadReads a checkpoint's geometry and metric without loading it.
dense_index.ts:1301 ↗
static metadataRead(
    path: string,
    config: DenseMetadataReadConfig = {},
): DenseMetadata

Parameters

pathstringThe manifest of a sharded checkpoint, or a single monolith.
configDenseMetadataReadConfigThe workers the read runs on.

Returns

The geometry and metric the checkpoint froze.
metadataReadBufferReads the geometry and metric of a monolith held in memory.
dense_index.ts:1312 ↗
static metadataReadBuffer(
    buffer: Uint8Array | Monolith,
    config: DenseMetadataReadBufferConfig = {},
): DenseMetadata

Parameters

bufferUint8Array | MonolithThe bytes DenseVerbs.saveBuffer produced.
configDenseMetadataReadBufferConfigThe workers the read runs on.

Returns

The geometry and metric the monolith froze.
containsVerifies the presence of one key in the index.
dense_index.ts:1007 ↗

Inherited from DenseIndexOn

contains(key: KeyLike): boolean

Binding-only sugar over count.

Parameters

keyKeyLikeThe identifier to be checked for presence in the index.

Returns

Whether the key is present.
cursorOpens a walk over the current contents, every pull on this receiver's executor.
dense_index.ts:1018 ↗

Inherited from DenseIndexOn

cursor(starts?: KeysLike): DenseCursor

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.

Parameters

startsKeysLikeThe keys to walk from; absent walks every entry.

Returns

The walk, which should be freed once pulling stops.
searchStreamOpens a resumable nearest-neighbor walk over one query row, every pull on this receiver's executor.
dense_index.ts:1032 ↗

Inherited from DenseIndexOn

searchStream(
    query: RowsView | Vector,
    config: DenseSearchStreamInitConfig = {},
): DenseSearchStream

Parameters

queryRowsView | VectorA single query row of the index geometry.
configDenseSearchStreamInitConfigThe breadth.

Returns

The stream, whose walk state lives until it is freed.
reserveClaims room for a total entry count ahead of insertion, a hint, since insertions grow the index on demand.
dense_index.ts:655 ↗

Inherited from DenseVerbs

reserve(capacity: number): Answer<Receiving, void>

Parameters

capacitynumberThe desired total capacity, the current size included.
clearErases every entry, keeping the reserved capacity and the frozen geometry.
dense_index.ts:663 ↗

Inherited from DenseVerbs

clear(): Answer<Receiving, void>
describeReports the frozen metadata and the current counters in one call.
dense_index.ts:674 ↗

Inherited from DenseVerbs

describe(): Answer<Receiving, DenseIndexDescription>

Returns

The geometry, the size, the capacity, the memory and the kernels in use.
healthAudits how much of the graph a search reaches from its landmarks.
dense_index.ts:688 ↗

Inherited from DenseVerbs

health(): Answer<Receiving, DenseReachability>

Returns

The population audited, and how much of it is reachable.
countCounts the entries stored under one key.
dense_index.ts:702 ↗

Inherited from DenseVerbs

count(key: KeyLike): Answer<Receiving, number>

Parameters

keyKeyLikeThe identifier to be enumerated.

Returns

How many entries the key holds.

Throws

Will throw an error if the key is spelled in none of the accepted forms.

insertAdd vectors to the index.
dense_index.ts:727 ↗

Inherited from DenseVerbs

insert(
    keys: KeysLike,
    vectors: RowsView | Vector,
    config: DenseInsertConfig = {},
): Answer<Receiving, void>

A typed array is one row, and a RowsView is a batch borrowed in place. A single key broadcasts across every row. A batch is all-or-nothing: a rejected one leaves the index untouched.

Rows keep whatever type they arrived in and the engine converts them into the index's own quantization, so the two are independent - a packed u1x8 or a bf16 row travels as bytes with its RowsView naming what they mean.

Parameters

keysKeysLikeInput identifiers for every vector.
vectorsRowsView | VectorOne row, or a run of rows of the index width.
configDenseInsertConfigPer-call breadth and telemetry sink.

Throws

Will throw an error if the length of keys doesn't match the number of vectors or if it's not a single key.

refineRe-selects the neighbors of every stored entry against the finished graph.
dense_index.ts:747 ↗

Inherited from DenseVerbs

refine(
    config: DenseRefineConfig = {},
): Answer<Receiving, DenseRefineReport>

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 breadth belongs here.

Parameters

configDenseRefineConfigPer-call breadth and telemetry sink.

Returns

The entries re-selected and changed, and the islands healed; zero changed means settled.
maxsimLate-interaction scoring of multi-row queries: every query row takes its best match within a key, and those maxima sum to that key's score.
dense_index.ts:805 ↗

Inherited from DenseVerbs

maxsim(
    queries: RowsView | Vector,
    rowsPerQuery: number,
    wanted: number,
    arena: Arena,
    config: DenseMaxsimConfig = {},
): Answer<Receiving, DenseProducts>

The run is over keys rather than rows, which is what an index built with valuesPerKey above one exists for. Runs on the inner-product kinds alone.

Parameters

queriesRowsView | VectorThe token rows of every query, end to end.
rowsPerQuerynumberRows in every query; uniform, so padded queries apply as is.
wantednumberDocuments per query, not per token row; zero keeps every document a query scored.
arenaArenaThe memory the scored keys land in, rewound by the call.
configDenseMaxsimConfigPer-call breadth and telemetry sink.

Returns

The scored keys, one run per query, largest first, viewed inside arena.

Throws

Will throw when the token rows do not split into whole queries.

getRetrieves the vectors stored under one key, one row per entry, in insertion order.
dense_index.ts:842 ↗

Inherited from DenseVerbs

get(
    key: KeyLike,
    arena: Arena,
    config: DenseGetConfig = {},
): Answer<Receiving, DenseVectors>

Rows land in arena and come back as a view into it, converted into scalar, shaped by the width and stride the call reports.

Parameters

keyKeyLikeThe identifier whose entries are exported.
arenaArenaThe memory the rows land in, rewound by the call.
configDenseGetConfigThe kind to export as.

Returns

The rows, laid end to end; empty when the key is absent.
multiplyMultiplies a matrix against the index, sparsely - every pair the walk scored, not only the nearest.
dense_index.ts:864 ↗

Inherited from DenseVerbs

multiply(
    left: RowsView | Vector,
    arena: Arena,
    config: DenseMultiplyConfig = {},
): Answer<Receiving, DenseProducts>

How many pairs a walk scores is known only once it ends, so the products land in arena and come back as views into it. The scores are unnormalized logits, so the handle must hold the inner-product kind.

Parameters

leftRowsView | VectorOne row, or a run of rows, at the index's own width.
arenaArenaThe memory the pairs and their offsets land in, rewound by the call.
configDenseMultiplyConfigPer-call pair bound, breadth and telemetry sink.

Returns

The pairs every left row's walk scored.
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.
dense_index.ts:893 ↗

Inherited from DenseVerbs

absorb(
    donors: DenseIndex[],
): Answer<Receiving, DenseRefineReport>

A donor the library consumed refuses every later verb with Status.Uninitialized; one it did not absorb, like a donor of another scalar refused with Status.ConfigMismatch, stays a live index the caller still owns, though its entries may already have moved here. Until a deferred absorb is final, every donor it names refuses every verb, and no other absorb can name it.

Parameters

donorsDenseIndex[]The indexes to absorb.

Returns

What the closing refine did to the graph.
splitSplits this index into the receivers, the inverse of absorb: graph-local parts keeping their edges, one per receiver, leaving this index empty but live.
dense_index.ts:912 ↗

Inherited from DenseVerbs

split(
    receivers: DenseIndex[],
    config: DenseSplitConfig = {},
): Answer<Receiving, DenseRefineReport>

Receivers must be empty and share the index's residency, scalar, class, metric, geometry and shard count, or the call refuses with Status.ConfigMismatch, moving nothing.

Parameters

receiversDenseIndex[]Two or more empty indexes, each receiving one part.
configDenseSplitConfigThe seed and breadth.

Returns

What the receivers' closing refines did to their graphs.
sampleDraws 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.
dense_index.ts:932 ↗

Inherited from DenseVerbs

sample(
    wanted: number,
    arena: Arena,
    config: DenseSampleConfig = {},
): Answer<Receiving, DenseDraws>

A larger wanted draws a superset, so a view grows one connected sample as it zooms. Only RAM samples yet.

Parameters

wantednumberThe upper bound on draws.
arenaArenaThe memory the draws land in, rewound by the call.
configDenseSampleConfigThe shown keys and their coordinates, the focus, the seed and the breadth.

Returns

The draws, viewed inside arena.
saveSaves the index in the layout the path extension picks - one .safegraphs monolith, or a .safegraphs.json manifest beside its shards.
dense_index.ts:963 ↗

Inherited from DenseVerbs

save(path: string): Answer<Receiving, void>

Parameters

pathstringFile path to write.

Throws

Will throw an error if path is not a string.

saveBufferWrites the index into memory as one monolith.
dense_index.ts:979 ↗

Inherited from DenseVerbs

saveBuffer(arena: Arena): Answer<Receiving, Monolith>

The bytes are what a checkpoint holds, so a service that keeps its indexes in an object store never touches the filesystem. They land in arena and come back as a view into it.

Parameters

arenaArenaThe memory the monolith lands in, rewound by the call.

Returns

One serialized monolith.
executorThe executor the verbs run on; absent runs them on the calling thread.
dense_index.ts:637 ↗

Inherited from DenseVerbs

readonly executor: Executor | undefined