| Title: | Fast Approximate Nearest Neighbour Search |
|---|---|
| Description: | R interface to the Rust crate 'ann-search-rs'. Build an exact or approximate nearest neighbour index once (HNSW, Annoy, IVF, NNDescent, Vamana and more), keep it in memory behind an R6 object and query it as often as you like. Multi-threaded via Rayon, with f32 or f64 storage. |
| Authors: | Gregor Lueg [aut, cre] |
| Maintainer: | Gregor Lueg <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.0.1 |
| Built: | 2026-10-08 18:23:53 UTC |
| Source: | https://github.com/GregorLueg/annsearchR |
Number of threads used for index builds and queries
ann_get_threads()ann_get_threads()
Integer.
Installs a dedicated thread pool for all subsequent builds and queries. Can be called as often as you like.
ann_set_threads(n)ann_set_threads(n)
n |
Integer. Number of threads. |
The previous thread count, invisibly.
Holds a pointer to an index living in Rust memory, plus the bits R needs to talk to it (metric, precision, shape). Never instantiated directly: use one of the algorithm classes such as HnswIndex, AnnoyIndex or ExhaustiveIndex.
The index itself is not serialised by saveRDS(). A restored object holds
a dead pointer and every method errors. Use $save() and
load_ann_index() instead.
nInteger. Number of indexed samples. Read-only.
dimInteger. Number of features. Read-only.
metricString. Distance metric. Read-only.
precisionString. "float" or "double". Read-only.
AnnIndex$predict()Find the k nearest indexed samples for each row of newdata.
AnnIndex$predict(newdata, k = 15L, return_dist = TRUE, .verbose = FALSE)
newdataNumeric matrix or data.frame. Queries x features, same number of features as the index.
kInteger. Number of neighbours.
return_distBoolean. Return the distances as well.
.verboseBoolean. Print progress from Rust.
A list with:
idx - Integer matrix, queries x k. 1-based row indices into the
indexed data. NA where fewer than k neighbours were found.
dist - Numeric matrix, queries x k, or NULL if
return_dist = FALSE. Inf where idx is NA.
AnnIndex$query_self()kNN graph over the indexed data itself. Uses each index's own self-query
path, which is faster than $predict() on the training data.
AnnIndex$query_self(k = 15L, return_dist = TRUE, .verbose = FALSE)
kInteger. Number of neighbours, including the sample itself.
return_distBoolean. Return the distances as well.
.verboseBoolean. Print progress from Rust.
A list with idx and dist, as for $predict(), with one row
per indexed sample.
AnnIndex$save()Write the index to a directory. Read it back with load_ann_index().
AnnIndex$save(dir)
dirString. Target directory, created if missing.
The object, invisibly.
AnnIndex$print()Print a one-line summary.
AnnIndex$print(...)
...Ignored.
The object, invisibly.
AnnIndex$clone()The objects of this class are cloneable with this method.
AnnIndex$clone(deep = FALSE)
deepWhether to make a deep clone.
Random projection forest, as in Spotify's Annoy. More trees means better
recall, a larger index and a slower build. Raise search_budget for recall
at query time. No Manhattan.
AnnIndex -> AnnoyIndex
search_budgetInteger or NULL. Candidates inspected per query.
AnnoyIndex$new()Build the index.
AnnoyIndex$new(
data,
metric = c("euclidean", "sqeuclidean", "cosine"),
n_trees = 25L,
search_budget = NULL,
seed = 42L,
precision = c("float", "double")
)
dataNumeric matrix or data.frame. Samples x features. An index
pointer is also accepted; load_ann_index() uses that path.
metricString. One of c("euclidean", "sqeuclidean", "cosine").
n_treesInteger. Number of trees in the forest.
search_budgetInteger or NULL. Candidates inspected per query.
NULL uses k * n_trees * 20. Can be changed after the build.
seedInteger. Fixes the random hyperplanes.
precisionString. "float" stores the data as f32, "double"
as f64.
AnnoyIndex$clone()The objects of this class are cloneable with this method.
AnnoyIndex$clone(deep = FALSE)
deepWhether to make a deep clone.
Bernhardsson, Annoy, https://github.com/spotify/annoy
x <- generate_clustered_data(1000L, 16L)$data idx <- AnnoyIndex$new(x, n_trees = 25L) res <- idx$predict(x[1:5, ], k = 10L)x <- generate_clustered_data(1000L, 16L)$data idx <- AnnoyIndex$new(x, n_trees = 25L) res <- idx$predict(x[1:5, ], k = 10L)
Metric tree of nested hyperspheres, pruned by the triangle inequality. Pays off on data with real cluster structure at moderate dimensionality. The default search budget (5% of the indexed points) makes it approximate; raise it for recall. Past about 10% recall plateaus and query time does not fall. No Manhattan.
AnnIndex -> BallTreeIndex
search_budgetInteger or NULL. Points inspected per query.
BallTreeIndex$new()Build the index.
BallTreeIndex$new(
data,
metric = c("euclidean", "sqeuclidean", "cosine"),
search_budget = NULL,
seed = 42L,
precision = c("float", "double")
)
dataNumeric matrix or data.frame. Samples x features. An index
pointer is also accepted; load_ann_index() uses that path.
metricString. One of c("euclidean", "sqeuclidean", "cosine").
search_budgetInteger or NULL. Points inspected per query.
NULL uses 5% of the indexed points. Can be changed after the build.
seedInteger. Fixes the pivot choice at each split.
precisionString. "float" stores the data as f32, "double"
as f64.
BallTreeIndex$clone()The objects of this class are cloneable with this method.
BallTreeIndex$clone(deep = FALSE)
deepWhether to make a deep clone.
Omohundro, ICSI Technical Report, 1989
x <- generate_clustered_data(1000L, 16L)$data idx <- BallTreeIndex$new(x) res <- idx$predict(x[1:5, ], k = 10L)x <- generate_clustered_data(1000L, 16L)$data idx <- BallTreeIndex$new(x) res <- idx$predict(x[1:5, ], k = 10L)
Exact search: every query is compared to every indexed sample, SIMD
accelerated and multi-threaded. The ground truth to measure the approximate
indices against, and faster than you might think up to a few hundred
thousand samples. Large query batches go through a blocked GEMM path, which
on macOS runs on Apple Accelerate, so it can outrun the thread count set by
ann_set_threads().
AnnIndex -> ExhaustiveIndex
ExhaustiveIndex$new()Build the index.
ExhaustiveIndex$new(
data,
metric = c("euclidean", "sqeuclidean", "cosine", "manhattan"),
precision = c("float", "double")
)
dataNumeric matrix or data.frame. Samples x features. An index
pointer is also accepted; load_ann_index() uses that path.
metricString. One of
c("euclidean", "sqeuclidean", "cosine", "manhattan").
precisionString. "float" stores the data as f32, "double"
as f64.
ExhaustiveIndex$clone()The objects of this class are cloneable with this method.
ExhaustiveIndex$clone(deep = FALSE)
deepWhether to make a deep clone.
x <- generate_clustered_data(1000L, 16L)$data idx <- ExhaustiveIndex$new(x) res <- idx$predict(x[1:5, ], k = 10L)x <- generate_clustered_data(1000L, 16L)$data idx <- ExhaustiveIndex$new(x) res <- idx$predict(x[1:5, ], k = 10L)
Foundation-model cell embeddings in the style of Geneformer or scGPT. A large shared mean offset puts every cell inside an anisotropy cone, a few axis-aligned rogue dimensions dominate dot products, each cell type varies in its own low-rank subspace, and a per-cell scale stands in for library size. The nastiest of the four generators.
generate_cell_embeddings(n, dim, n_clusters = 25L, seed = 42L)generate_cell_embeddings(n, dim, n_clusters = 25L, seed = 42L)
n |
Integer. Number of samples. |
dim |
Integer. Number of features. |
n_clusters |
Integer. Number of clusters. |
seed |
Integer. Random seed. |
A list with:
data - Numeric matrix, n x dim.
labels - Integer vector of 1-based cluster labels.
dat <- generate_cell_embeddings(1000L, 64L, n_clusters = 8L)dat <- generate_cell_embeddings(1000L, 64L, n_clusters = 8L)
Separated Gaussian clusters, with a fifth of the points on thin bridges between neighbouring clusters so the boundaries aren't trivially clean. The baseline of the four generators.
All generators are the ones behind the Rust crate's benchmark tables. They draw in f32 like the crate's gridsearch examples and the Python bindings, so the same seed gives the same points in all three.
generate_clustered_data(n, dim, n_clusters = 25L, seed = 42L)generate_clustered_data(n, dim, n_clusters = 25L, seed = 42L)
n |
Integer. Number of samples. |
dim |
Integer. Number of features. |
n_clusters |
Integer. Number of clusters. |
seed |
Integer. Random seed. |
A list with:
data - Numeric matrix, n x dim.
labels - Integer vector of 1-based cluster labels.
dat <- generate_clustered_data(1000L, 16L, n_clusters = 5L) table(dat$labels)dat <- generate_clustered_data(1000L, 16L, n_clusters = 5L) table(dat$labels)
Cell types on an intrinsic_dim-dimensional manifold, isometrically
embedded in dim ambient dimensions with a touch of noise. Types are
grouped into lineages, with curved differentiation trajectories running
between types of the same lineage.
generate_low_rank_data( n, dim, n_clusters = 25L, intrinsic_dim = 16L, seed = 42L )generate_low_rank_data( n, dim, n_clusters = 25L, intrinsic_dim = 16L, seed = 42L )
n |
Integer. Number of samples. |
dim |
Integer. Number of features. |
n_clusters |
Integer. Number of clusters. |
intrinsic_dim |
Integer. True dimensionality of the manifold. Must not
exceed |
seed |
Integer. Random seed. |
A list with:
data - Numeric matrix, n x dim.
labels - Integer vector of 1-based cluster labels.
dat <- generate_low_rank_data(1000L, 64L, intrinsic_dim = 8L)dat <- generate_low_rank_data(1000L, 64L, intrinsic_dim = 8L)
Hierarchical navigable small world graph. The usual first choice: high
recall at low query latency. Raise ef_search for recall at query time,
ef_construction for a better graph at build time.
Nodes are inserted in parallel, so two builds with the same seed give
slightly different graphs. Need bit-identical results? Build with
ann_set_threads(1L).
AnnIndex -> HnswIndex
ef_searchInteger. Beam width at query time.
HnswIndex$new()Build the index.
HnswIndex$new(
data,
metric = c("euclidean", "sqeuclidean", "cosine", "manhattan"),
m = 16L,
ef_construction = 200L,
ef_search = 50L,
seed = 42L,
precision = c("float", "double"),
.verbose = FALSE
)
dataNumeric matrix or data.frame. Samples x features. An index
pointer is also accepted; load_ann_index() uses that path.
metricString. One of
c("euclidean", "sqeuclidean", "cosine", "manhattan").
mInteger. Edges per node on the upper layers, 2 * m on layer
0. 16 suits most data; 32 to 48 helps in high dimensions.
ef_constructionInteger. Candidate list width during the build. Better graph, slower build, no cost at query time.
ef_searchInteger. Beam width at query time. Raised to k
internally if smaller. Can be changed after the build.
seedInteger. Fixes the layer assignment.
precisionString. "float" stores the data as f32, "double"
as f64.
.verboseBoolean. Print build progress from Rust.
HnswIndex$clone()The objects of this class are cloneable with this method.
HnswIndex$clone(deep = FALSE)
deepWhether to make a deep clone.
Malkov & Yashunin, IEEE TPAMI, 2020
x <- generate_clustered_data(1000L, 16L)$data idx <- HnswIndex$new(x, metric = "cosine") idx$ef_search <- 100L res <- idx$predict(x[1:5, ], k = 10L)x <- generate_clustered_data(1000L, 16L)$data idx <- HnswIndex$new(x, metric = "cosine") idx$ef_search <- 100L res <- idx$predict(x[1:5, ], k = 10L)
Inverted file over k-means Voronoi cells. Cheap to build and easy to tune:
nlist sets how finely the space is cut, nprobe how many cells a query
visits. The smallest of the unquantised approximate indices. No Manhattan.
AnnIndex -> IvfIndex
nprobeInteger or NULL. Cells visited per query.
IvfIndex$new()Build the index.
IvfIndex$new(
data,
metric = c("euclidean", "sqeuclidean", "cosine"),
nlist = NULL,
nprobe = NULL,
kmeans_iters = NULL,
kmeans_balanced = FALSE,
seed = 42L,
precision = c("float", "double"),
.verbose = FALSE
)
dataNumeric matrix or data.frame. Samples x features. An index
pointer is also accepted; load_ann_index() uses that path.
metricString. One of c("euclidean", "sqeuclidean", "cosine").
nlistInteger or NULL. Number of Voronoi cells. NULL uses
sqrt(n).
nprobeInteger or NULL. Cells visited per query. NULL uses
sqrt(nlist). Can be changed after the build.
kmeans_itersInteger or NULL. Lloyd iterations. NULL uses 30.
kmeans_balancedBoolean. Reseed starved centroids each iteration.
seedInteger. Fixes the k-means initialisation.
precisionString. "float" stores the data as f32, "double"
as f64.
.verboseBoolean. Print build progress from Rust.
IvfIndex$clone()The objects of this class are cloneable with this method.
IvfIndex$clone(deep = FALSE)
deepWhether to make a deep clone.
Jégou, Douze & Schmid, IEEE TPAMI, 2011
x <- generate_clustered_data(1000L, 16L)$data idx <- IvfIndex$new(x) idx$nprobe <- 10L res <- idx$predict(x[1:5, ], k = 10L)x <- generate_clustered_data(1000L, 16L)$data idx <- IvfIndex$new(x) idx$nprobe <- 10L res <- idx$predict(x[1:5, ], k = 10L)
Forest of randomised kd spill-trees. Same trade as AnnoyIndex with axis-aligned splits instead of random hyperplanes: more trees means better recall and a larger index. Points near a split land in both children, which lets one descent recover neighbours a hard split would separate. The one tree index here that supports Manhattan.
AnnIndex -> KdTreeIndex
search_budgetInteger or NULL. Candidates inspected per query.
KdTreeIndex$new()Build the index.
KdTreeIndex$new(
data,
metric = c("euclidean", "sqeuclidean", "cosine", "manhattan"),
n_trees = 25L,
search_budget = NULL,
seed = 42L,
precision = c("float", "double")
)
dataNumeric matrix or data.frame. Samples x features. An index
pointer is also accepted; load_ann_index() uses that path.
metricString. One of
c("euclidean", "sqeuclidean", "cosine", "manhattan").
n_treesInteger. Number of trees in the forest.
search_budgetInteger or NULL. Candidates inspected per query.
NULL uses k * n_trees * 20. Can be changed after the build.
seedInteger. Fixes the split dimensions.
precisionString. "float" stores the data as f32, "double"
as f64.
KdTreeIndex$clone()The objects of this class are cloneable with this method.
KdTreeIndex$clone(deep = FALSE)
deepWhether to make a deep clone.
Muja & Lowe, IEEE TPAMI, 2014
x <- generate_clustered_data(1000L, 16L)$data idx <- KdTreeIndex$new(x) res <- idx$predict(x[1:5, ], k = 10L)x <- generate_clustered_data(1000L, 16L)$data idx <- KdTreeIndex$new(x) res <- idx$predict(x[1:5, ], k = 10L)
Exact search over a k-means partition, pruning whole clusters by the triangle inequality. Same answers as ExhaustiveIndex; whether it is faster depends on how well the data clusters, so time both. No Manhattan.
AnnIndex -> KmknnIndex
KmknnIndex$new()Build the index.
KmknnIndex$new(
data,
metric = c("euclidean", "sqeuclidean", "cosine"),
nlist = NULL,
kmeans_iters = NULL,
kmeans_balanced = FALSE,
seed = 42L,
precision = c("float", "double"),
.verbose = FALSE
)
dataNumeric matrix or data.frame. Samples x features. An index
pointer is also accepted; load_ann_index() uses that path.
metricString. One of c("euclidean", "sqeuclidean", "cosine").
nlistInteger or NULL. Number of k-means clusters. NULL uses
sqrt(n).
kmeans_itersInteger or NULL. Lloyd iterations. NULL uses 30.
kmeans_balancedBoolean. Reseed starved centroids each iteration.
seedInteger. Fixes the k-means initialisation.
precisionString. "float" stores the data as f32, "double"
as f64.
.verboseBoolean. Print build progress from Rust.
KmknnIndex$clone()The objects of this class are cloneable with this method.
KmknnIndex$clone(deep = FALSE)
deepWhether to make a deep clone.
Wang, IEEE ICDMW, 2012
x <- generate_clustered_data(1000L, 16L)$data idx <- KmknnIndex$new(x) res <- idx$predict(x[1:5, ], k = 10L)x <- generate_clustered_data(1000L, 16L)$data idx <- KmknnIndex$new(x) res <- idx$predict(x[1:5, ], k = 10L)
Fraction of the true k nearest neighbours that the approximate search
found, averaged over queries. Order within a row does not matter.
knn_recall(truth, approx, k = NULL)knn_recall(truth, approx, k = NULL)
truth |
Integer matrix. Queries x k true neighbour indices, e.g. the
|
approx |
Integer matrix. Queries x k approximate neighbour indices. |
k |
Optional integer. Only the first |
Numeric between 0 and 1.
$save()
Reads the index bundle and its R-side parameters (metric, search knobs) back into the matching AnnIndex subclass. The float width is read from the bundle. Bundles are tied to the on-disk format version of the underlying Rust crate; a format bump means rebuilding.
load_ann_index(dir)load_ann_index(dir)
dir |
String. Directory written by |
An AnnIndex subclass, ready to query.
Multi-probe locality-sensitive hashing over random projections. By far the
cheapest index to build here, and the weakest on recall for the query time
it costs; the one index whose query can come out slower than brute force.
Fewer bits_per_hash widens the buckets, more num_tables trades memory
for recall. No Manhattan.
AnnIndex -> LshIndex
n_probeInteger or NULL. Buckets probed per table.
max_candidatesInteger or NULL. Cap on candidates scored.
LshIndex$new()Build the index.
LshIndex$new(
data,
metric = c("euclidean", "sqeuclidean", "cosine"),
num_tables = 8L,
bits_per_hash = 12L,
slot_bits = NULL,
n_probe = NULL,
max_candidates = NULL,
seed = 42L,
precision = c("float", "double")
)
dataNumeric matrix or data.frame. Samples x features. An index
pointer is also accepted; load_ann_index() uses that path.
metricString. One of c("euclidean", "sqeuclidean", "cosine").
num_tablesInteger. Independent hash tables.
bits_per_hashInteger. Total bits in a bucket code.
slot_bitsInteger or NULL. Bits each quantised projection
contributes. NULL uses 1 for cosine and 2 otherwise.
n_probeInteger or NULL. Buckets probed per table. NULL uses
one per projection. Can be changed after the build.
max_candidatesInteger or NULL. Cap on candidates scored per
query. NULL means no cap. Can be changed after the build.
seedInteger. Fixes the random projections.
precisionString. "float" stores the data as f32, "double"
as f64.
LshIndex$clone()The objects of this class are cloneable with this method.
LshIndex$clone(deep = FALSE)
deepWhether to make a deep clone.
Lv et al., VLDB, 2007
x <- generate_clustered_data(1000L, 16L)$data idx <- LshIndex$new(x) res <- idx$predict(x[1:5, ], k = 10L)x <- generate_clustered_data(1000L, 16L)$data idx <- LshIndex$new(x) res <- idx$predict(x[1:5, ], k = 10L)
Builds the kNN graph directly by iterative local join. Want the kNN graph of
the data itself (UMAP, clustering)? $extract_knn() hands back the
converged graph without any search, far cheaper than $query_self(). It
reads the post-pruning graph, so leave diversify_prob at 0 if extraction
is the point.
AnnIndex -> NNDescentIndex
ef_searchInteger or NULL. Beam width at query time.
k_graphInteger. Degree of the built graph. Read-only.
NNDescentIndex$new()Build the index.
NNDescentIndex$new(
data,
metric = c("euclidean", "sqeuclidean", "cosine", "manhattan"),
k_graph = 15L,
delta = 0.001,
diversify_prob = 0,
max_iter = NULL,
max_candidates = NULL,
n_trees = NULL,
ef_search = NULL,
seed = 42L,
precision = c("float", "double"),
.verbose = FALSE
)
dataNumeric matrix or data.frame. Samples x features. An index
pointer is also accepted; load_ann_index() uses that path.
metricString. One of
c("euclidean", "sqeuclidean", "cosine", "manhattan").
k_graphInteger. Neighbours per node in the graph being built.
deltaNumeric. Convergence threshold: descent stops once the fraction of updated edges falls below it.
diversify_probNumeric between 0 and 1. Probability of pruning an
occluded edge after descent. 0 disables pruning.
max_iterInteger or NULL. Iteration cap. NULL uses
max(round(log2(n)), 5).
max_candidatesInteger or NULL. Neighbours sampled per node per
local join. NULL uses min(k_graph, 60).
n_treesInteger or NULL. Random projection trees for the seed
graph. NULL uses min(5 + round(n^0.25), 12).
ef_searchInteger or NULL. Beam width at query time. NULL
uses clamp(2 * k, 50, 200). Can be changed after the build.
seedInteger. Fixes the seed graph and the sampling.
precisionString. "float" stores the data as f32, "double"
as f64.
.verboseBoolean. Print build progress from Rust.
NNDescentIndex$extract_knn()The converged kNN graph over the indexed data, read straight off the index. No search involved.
NNDescentIndex$extract_knn(k = NULL, include_self = TRUE, return_dist = TRUE)
kInteger or NULL. Row length, including the sample itself when
include_self = TRUE. NULL uses the graph degree k_graph.
include_selfBoolean. Put each sample first at distance 0, as
$query_self() does.
return_distBoolean. Return the distances as well.
A list with idx and dist, as for $predict(), with one row
per indexed sample. Rows the graph cannot fill are NA padded.
NNDescentIndex$clone()The objects of this class are cloneable with this method.
NNDescentIndex$clone(deep = FALSE)
deepWhether to make a deep clone.
Dong, Moses & Li, WWW, 2011
x <- generate_clustered_data(1000L, 16L)$data idx <- NNDescentIndex$new(x, k_graph = 15L) knn <- idx$extract_knn()x <- generate_clustered_data(1000L, 16L)$data idx <- NNDescentIndex$new(x, k_graph = 15L) knn <- idx$extract_knn()
Navigating spreading-out graph. Builds an NN-Descent kNN graph of degree
knn_k first, then refines it into a sparse monotonic graph. Smallest of
the graph indices; pays for it with a double build.
AnnIndex -> NsgIndex
ef_searchInteger or NULL. Beam width at query time.
NsgIndex$new()Build the index.
NsgIndex$new(
data,
metric = c("euclidean", "sqeuclidean", "cosine", "manhattan"),
r = 32L,
l_build = 100L,
c = 500L,
knn_k = 64L,
ef_search = NULL,
seed = 42L,
precision = c("float", "double"),
.verbose = FALSE
)
dataNumeric matrix or data.frame. Samples x features. An index
pointer is also accepted; load_ann_index() uses that path.
metricString. One of
c("euclidean", "sqeuclidean", "cosine", "manhattan").
rInteger. Maximum out-degree of the refined graph.
l_buildInteger. Candidate list width while refining.
cInteger. Candidate pool size per node before pruning.
knn_kInteger. Degree of the NN-Descent graph built first. Wants
to be comfortably above r.
ef_searchInteger or NULL. Beam width at query time. NULL
uses 100. Can be changed after the build.
seedInteger. Fixes the initial graph and navigating node.
precisionString. "float" stores the data as f32, "double"
as f64.
.verboseBoolean. Print build progress from Rust.
NsgIndex$clone()The objects of this class are cloneable with this method.
NsgIndex$clone(deep = FALSE)
deepWhether to make a deep clone.
Fu et al., VLDB, 2019
x <- generate_clustered_data(1000L, 16L)$data idx <- NsgIndex$new(x) res <- idx$predict(x[1:5, ], k = 10L)x <- generate_clustered_data(1000L, 16L)$data idx <- NsgIndex$new(x) res <- idx$predict(x[1:5, ], k = 10L)
S3 wrapper around the $predict() method of an AnnIndex.
## S3 method for class 'AnnIndex' predict(object, newdata, k = 15L, ...)## S3 method for class 'AnnIndex' predict(object, newdata, k = 15L, ...)
object |
An AnnIndex. |
newdata |
Numeric matrix or data.frame. Queries x features. |
k |
Integer. Number of neighbours. |
... |
Passed on to |
A list with idx and dist. See AnnIndex.
Builds and prunes a navigable graph in one pass, skipping the separate
refinement step NsgIndex pays for. r caps the out-degree, ef_search
is the recall knob. On Gaussian benchmark data it tops out around 0.97
recall, so check it reaches what you need.
AnnIndex -> RnnDescentIndex
ef_searchInteger or NULL. Beam width at query time.
k_searchInteger or NULL. Neighbours expanded per hop.
RnnDescentIndex$new()Build the index.
RnnDescentIndex$new(
data,
metric = c("euclidean", "sqeuclidean", "cosine", "manhattan"),
s = 20L,
r = 96L,
t1 = 4L,
t2 = 15L,
n_trees = NULL,
ef_search = NULL,
k_search = NULL,
seed = 42L,
precision = c("float", "double"),
.verbose = FALSE
)
dataNumeric matrix or data.frame. Samples x features. An index
pointer is also accepted; load_ann_index() uses that path.
metricString. One of
c("euclidean", "sqeuclidean", "cosine", "manhattan").
sInteger. Neighbours sampled per node per local join.
rInteger. Maximum out-degree after pruning.
t1Integer. Outer iterations.
t2Integer. Inner descent iterations per outer one.
n_treesInteger or NULL. Random projection trees for the seed
graph. NULL uses min(5 + n^0.25 / 2, 16).
ef_searchInteger or NULL. Beam width at query time. NULL
uses 100. Can be changed after the build.
k_searchInteger or NULL. Neighbours expanded per hop. NULL
uses 32, capped at r. Can be changed after the build.
seedInteger. Fixes the seed graph and the sampling.
precisionString. "float" stores the data as f32, "double"
as f64.
.verboseBoolean. Print build progress from Rust.
RnnDescentIndex$clone()The objects of this class are cloneable with this method.
RnnDescentIndex$clone(deep = FALSE)
deepWhether to make a deep clone.
Ono & Matsui, ACM MM, 2023
x <- generate_clustered_data(1000L, 16L)$data idx <- RnnDescentIndex$new(x) res <- idx$predict(x[1:5, ], k = 10L)x <- generate_clustered_data(1000L, 16L)$data idx <- RnnDescentIndex$new(x) res <- idx$predict(x[1:5, ], k = 10L)
IVF with spilling: every point also lands in a second cell, picked by a rule
that accounts for the residual it carries in its first. Buys recall at a
given nprobe over IvfIndex, but scans about twice the candidates per
probe, so compare the two on query time, not on nprobe. No Manhattan.
AnnIndex -> SoarIndex
nprobeInteger or NULL. Cells visited per query.
SoarIndex$new()Build the index.
SoarIndex$new(
data,
metric = c("euclidean", "sqeuclidean", "cosine"),
nlist = NULL,
nprobe = NULL,
rule = NULL,
rule_param = NULL,
kmeans_iters = NULL,
kmeans_balanced = FALSE,
seed = 42L,
precision = c("float", "double"),
.verbose = FALSE
)
dataNumeric matrix or data.frame. Samples x features. An index
pointer is also accepted; load_ann_index() uses that path.
metricString. One of c("euclidean", "sqeuclidean", "cosine").
nlistInteger or NULL. Number of Voronoi cells. NULL uses
sqrt(n).
nprobeInteger or NULL. Cells visited per query. NULL uses
sqrt(nlist). Can be changed after the build.
ruleString or NULL. Secondary assignment rule, one of
c("nearest", "shifted", "orthogonal"). NULL picks orthogonal for
cosine and shifted otherwise.
rule_paramNumeric or NULL. mu for the shifted rule, lambda
for the orthogonal one. NULL uses 0.5 and 1.0 respectively.
kmeans_itersInteger or NULL. Lloyd iterations. NULL uses 30.
kmeans_balancedBoolean. Reseed starved centroids each iteration.
seedInteger. Fixes the k-means initialisation.
precisionString. "float" stores the data as f32, "double"
as f64.
.verboseBoolean. Print build progress from Rust.
SoarIndex$clone()The objects of this class are cloneable with this method.
SoarIndex$clone(deep = FALSE)
deepWhether to make a deep clone.
Sun et al., NeurIPS, 2023
x <- generate_clustered_data(1000L, 16L)$data idx <- SoarIndex$new(x) res <- idx$predict(x[1:5, ], k = 10L)x <- generate_clustered_data(1000L, 16L)$data idx <- SoarIndex$new(x) res <- idx$predict(x[1:5, ], k = 10L)
Draws rows from x and adds light Gaussian noise (sd 0.05). Querying an
index with the rows it was built from flatters it: every query has an exact
hit at distance zero. This puts the queries near the data instead of on it.
The data is cast to f32 first, as in the Python bindings.
subsample_queries(x, n, seed = 42L)subsample_queries(x, n, seed = 42L)
x |
Numeric matrix, samples x features. |
n |
Integer. Number of rows to draw, capped at |
seed |
Integer. Random seed for the draw and the noise. |
Numeric matrix with min(n, nrow(x)) rows and ncol(x) columns.
dat <- generate_clustered_data(1000L, 16L)$data query <- subsample_queries(dat, 100L)dat <- generate_clustered_data(1000L, 16L)$data query <- subsample_queries(dat, 100L)
The flat graph from DiskANN: out-degree r, pruned in two passes with the
relaxed-neighbour rule. Builds slower than HnswIndex at its cheapest
setting, though the gap closes once you match on recall, and the index is a
few per cent smaller.
AnnIndex -> VamanaIndex
ef_searchInteger or NULL. Beam width at query time.
VamanaIndex$new()Build the index.
VamanaIndex$new(
data,
metric = c("euclidean", "sqeuclidean", "cosine", "manhattan"),
r = 48L,
l_build = 100L,
alpha_pass1 = 1,
alpha_pass2 = 1.2,
ef_search = NULL,
seed = 42L,
precision = c("float", "double")
)
dataNumeric matrix or data.frame. Samples x features. An index
pointer is also accepted; load_ann_index() uses that path.
metricString. One of
c("euclidean", "sqeuclidean", "cosine", "manhattan").
rInteger. Maximum out-degree.
l_buildInteger. Candidate list width during the build.
alpha_pass1Numeric. Relaxed-neighbour factor on the first
pruning pass. 1 is the plain rule.
alpha_pass2Numeric. Same on the second pass. Above 1 keeps longer edges, which keeps the graph navigable from far away.
ef_searchInteger or NULL. Beam width at query time. NULL
uses 75. Can be changed after the build.
seedInteger. Fixes the entry point and pruning order.
precisionString. "float" stores the data as f32, "double"
as f64.
VamanaIndex$clone()The objects of this class are cloneable with this method.
VamanaIndex$clone(deep = FALSE)
deepWhether to make a deep clone.
Subramanya et al., NeurIPS, 2019
x <- generate_clustered_data(1000L, 16L)$data idx <- VamanaIndex$new(x) res <- idx$predict(x[1:5, ], k = 10L)x <- generate_clustered_data(1000L, 16L)$data idx <- VamanaIndex$new(x) res <- idx$predict(x[1:5, ], k = 10L)