## -----------------------------------------------------------------------------
#| label: threads
#| include: false
annsearchR::ann_set_threads(2L)


## -----------------------------------------------------------------------------
#| label: setup
library(annsearchR)


## -----------------------------------------------------------------------------
#| label: data
dat <- generate_clustered_data(20000L, 32L, n_clusters = 20L, seed = 42L)$data
query <- dat[1:500, ]
train <- dat[-(1:500), ]


## -----------------------------------------------------------------------------
#| label: build-hnsw
hnsw <- HnswIndex$new(train, metric = "euclidean")
hnsw

res <- hnsw$predict(query, k = 10L)
str(res)


## -----------------------------------------------------------------------------
#| label: predict-s3
identical(predict(hnsw, query, k = 10L), res)


## -----------------------------------------------------------------------------
#| label: recall
truth <- ExhaustiveIndex$new(train)$predict(query, k = 10L)$idx
knn_recall(truth, res$idx)


## -----------------------------------------------------------------------------
#| label: ef-search-sweep
for (ef in c(10L, 20L, 50L, 100L)) {
  hnsw$ef_search <- ef
  t <- system.time(r <- hnsw$predict(query, k = 10L))[["elapsed"]]
  cat(sprintf(
    "ef_search = %3d   recall %.3f   %.3fs\n",
    ef,
    knn_recall(truth, r$idx),
    t
  ))
}


## -----------------------------------------------------------------------------
#| label: compare-indices
indices <- list(
  exhaustive = ExhaustiveIndex,
  kmknn = KmknnIndex,
  annoy = AnnoyIndex,
  kdtree = KdTreeIndex,
  balltree = BallTreeIndex,
  hnsw = HnswIndex,
  ivf = IvfIndex,
  soar = SoarIndex,
  lsh = LshIndex,
  nndescent = NNDescentIndex,
  vamana = VamanaIndex,
  nsg = NsgIndex,
  rnndescent = RnnDescentIndex
)

comparison <- do.call(
  rbind,
  lapply(names(indices), \(nm) {
    t_build <- system.time(idx <- indices[[nm]]$new(train))[["elapsed"]]
    t_query <- system.time(r <- idx$predict(query, k = 10L))[["elapsed"]]
    data.frame(
      index = nm,
      build_s = t_build,
      query_s = t_query,
      recall = round(knn_recall(truth, r$idx), 3)
    )
  })
)
comparison


## -----------------------------------------------------------------------------
#| label: query-self
knn <- hnsw$query_self(k = 15L)
dim(knn$idx)
all(knn$idx[, 1] == seq_len(nrow(train)))


## -----------------------------------------------------------------------------
#| label: extract-knn
nnd <- NNDescentIndex$new(train, k_graph = 15L)
graph <- nnd$extract_knn()

truth_self <- ExhaustiveIndex$new(train)$query_self(k = 15L)$idx
knn_recall(truth_self, graph$idx)


## -----------------------------------------------------------------------------
#| label: manhattan-error
#| error: true
try({
AnnoyIndex$new(train, metric = "manhattan")
})


## -----------------------------------------------------------------------------
#| label: precision
hnsw64 <- HnswIndex$new(train, precision = "double")
hnsw64$precision


## -----------------------------------------------------------------------------
#| label: set-threads
old <- ann_set_threads(1L)
ann_get_threads()
ann_set_threads(0L) # back to all cores


## -----------------------------------------------------------------------------
#| label: dead-pointer
#| error: true
try({
restored <- unserialize(serialize(hnsw, NULL))
restored$predict(query, k = 10L)
})


## -----------------------------------------------------------------------------
#| label: save-load
dir <- tempfile()
hnsw$ef_search <- 80L
hnsw$save(dir)

loaded <- load_ann_index(dir)
loaded
identical(loaded$predict(query, k = 10L), hnsw$predict(query, k = 10L))


## -----------------------------------------------------------------------------
#| label: reset-threads
#| include: false
annsearchR::ann_set_threads(0L)

