refine re-selects the neighbors of every stored entry against the finished graph and returns a DenseRefineReport: the entries re-selected and changed, the islands found and reconnected, and the closing audit.
A cheap insertion pass followed by one refinement reaches a better graph in less total time than one expensive insertion pass.
Zero changed means the graph has settled, and on a RAM index searches may run throughout.
while ((try index.on(executor).refine(.{ .expansion_factor = 128 })).changed != 0) {}
absorb moves donor handles into one index, one after another, keeping their graphs rather than re-inserting their vectors, then refines every shard that took one across the appended part.
A donor must share the index’s residency, scalar, metric and geometry, or the call answers Error.ConfigMismatch.
Each donor it absorbs is consumed and nulled in place, so freeing every donor afterwards is safe, and a stop between donors, or a failure, leaves the rest live for their deinit to free.
var donors = [_]usearch.DenseIndex{ first, second };_ = try index.on(executor).absorb(&donors, .{});for (&donors) |*donor| donor.deinit(); // frees only the donors a stop or a failure left live
split is the inverse: it hands graph-local parts of the index to empty receivers of the same configuration, one part each, keeping their edges and leaving the index empty but live.
It reports the receivers’ closing refines as refine does.