Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 44 additions & 7 deletions src/main/java/com/nvidia/cuvs/lucene/AcceleratedHNSWParams.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import com.nvidia.cuvs.CagraIndexParams.CagraGraphBuildAlgo;
import com.nvidia.cuvs.CagraIndexParams.CuvsDistanceType;
import com.nvidia.cuvs.CagraIndexParams.HnswHeuristicType;
import com.nvidia.cuvs.CuVSIvfPqParams;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
Expand All @@ -17,9 +18,8 @@ public class AcceleratedHNSWParams {

public static enum Strategy {
/*
* This strategy allows for automatic selection of the underlying CAGRA build algorithm.
* With this strategy we use NN_DESCENT for dataset less than 5M vectors, else we use IVF_PQ.
* Indexing parameters, especially for IVF_PQ, are heuristically identified automatically.
* This strategy delegates the derivation of the CAGRA build parameters (graph degrees, build
* algorithm and its parameters) to cuVS, based on HNSW-equivalent maxConn and beamWidth.
*
* This is the default and the recommended strategy.
*/
Expand Down Expand Up @@ -64,6 +64,8 @@ public static enum Strategy {
public static final Strategy DEFAULT_STRATEGY = Strategy.HEURISTIC;
public static final CuvsDistanceType DEFAULT_CUVS_DISTANCE_TYPE = CuvsDistanceType.L2Expanded;
public static final int DEFAULT_NN_DESCENT_NUM_ITERATIONS = 20;
public static final HnswHeuristicType DEFAULT_HNSW_HEURISTIC_TYPE =
HnswHeuristicType.SAME_GRAPH_FOOTPRINT;

public static final Supplier<CuVSIvfPqParams> DEFAULT_IVF_PQ_PARAMS =
() -> {
Expand All @@ -88,24 +90,25 @@ public static enum Strategy {
private final Strategy strategy;
private final CuvsDistanceType cuvsDistanceType;
private final int nnDescentNumIterations;
private final HnswHeuristicType hnswHeuristicType;

/**
* Constructs an instance of {@link AcceleratedHNSWParams} with specific parameter values.
*
* @param writerThreads Number of cuVS writer threads to use.
* @param intermediateGraphDegree The intermediate graph degree while building the CAGRA index.
* @param graphdegree The graph degree to use while building the CAGRA index.
* @param indexType The type of index to build - CAGRA, BRUTEFORCE, or both.
* @param hnswLayers The number of HNSW layers to build in the HNSW index.
* @param maxConn The max connection parameter used when building HNSW index with the fallback mechanism.
* @param beamWidth The beam width parameter used when building HNSW index with the fallback mechanism.
* @param cagraGraphBuildAlgo The CAGRA graph build algorithm to use [NN_DESCENT, IVF_PQ].
* @param cuVSIvfPqParams An instance of CuVSIvfPqParams containing IVF_PQ specific parameters.
* @param numMergeWorkers The number of merge workers to use with the fallback mechanism.
* @param mergeExec The instance of {@link ExecutorService} to use with the fallback mechanism.
* @param strategy either HEURISTIC [Default] that automatically chooses build algorithm and its parameters based on data set size or CUSTOM that uses the parameters passed though this class.
* @param strategy either HEURISTIC [Default] that delegates the CAGRA build parameters to cuVS (derived from the HNSW-equivalent maxConn and beamWidth) or CUSTOM that uses the parameters passed through this class.
* @param cuvsDistanceType the cuvsDistanceType. The default option is L2Expanded.
* @param nnDescentNumIterations the number of Iterations to run if building with NN_DESCENT.
* @param hnswHeuristicType the heuristic cuVS applies when deriving the CAGRA build parameters from maxConn and beamWidth under the HEURISTIC strategy.
*/
private AcceleratedHNSWParams(
int writerThreads,
Expand All @@ -120,7 +123,8 @@ private AcceleratedHNSWParams(
ExecutorService mergeExec,
Strategy strategy,
CuvsDistanceType cuvsDistanceType,
int nnDescentNumIterations) {
int nnDescentNumIterations,
HnswHeuristicType hnswHeuristicType) {
super();
this.writerThreads = writerThreads;
this.intermediateGraphDegree = intermediateGraphDegree;
Expand All @@ -135,6 +139,7 @@ private AcceleratedHNSWParams(
this.strategy = strategy;
this.cuvsDistanceType = cuvsDistanceType;
this.nnDescentNumIterations = nnDescentNumIterations;
this.hnswHeuristicType = hnswHeuristicType;
}

/**
Expand Down Expand Up @@ -257,6 +262,16 @@ public int getNNDescentNumIterations() {
return nnDescentNumIterations;
}

/**
* Get the heuristic cuVS applies when deriving the CAGRA build parameters from maxConn and
* beamWidth. Only consulted under the {@link Strategy#HEURISTIC} strategy.
*
* @return the {@link HnswHeuristicType} to hand to cuVS
*/
public HnswHeuristicType getHnswHeuristicType() {
return hnswHeuristicType;
}

@Override
public String toString() {
return "AcceleratedHNSWParams [writerThreads="
Expand Down Expand Up @@ -285,6 +300,8 @@ public String toString() {
+ cuvsDistanceType
+ ", nnDescentNumIterations="
+ nnDescentNumIterations
+ ", hnswHeuristicType="
+ hnswHeuristicType
+ "]";
}

Expand All @@ -306,6 +323,7 @@ public static class Builder {
private Strategy strategy = DEFAULT_STRATEGY;
private CuvsDistanceType cuvsDistanceType = DEFAULT_CUVS_DISTANCE_TYPE;
private int nnDescentNumIterations = DEFAULT_NN_DESCENT_NUM_ITERATIONS;
private HnswHeuristicType hnswHeuristicType = DEFAULT_HNSW_HEURISTIC_TYPE;

/**
* Set the number of cuVS writer threads while building the index
Expand Down Expand Up @@ -474,6 +492,21 @@ public Builder withNNDescentNumIterations(int nnDescentNumIterations) {
return this;
}

/**
* Set the heuristic cuVS applies when deriving the CAGRA build parameters from maxConn and
* beamWidth. Only consulted under the {@link Strategy#HEURISTIC} strategy.
*
* Default value - SAME_GRAPH_FOOTPRINT, which targets a CAGRA graph of the same on-disk size as
* the equivalent HNSW graph (graph degree = 2 * maxConn).
*
* @param hnswHeuristicType the {@link HnswHeuristicType} to hand to cuVS
* @return instance of {@link Builder}
*/
public Builder withHnswHeuristicType(HnswHeuristicType hnswHeuristicType) {
this.hnswHeuristicType = hnswHeuristicType;
return this;
}

/**
* Validates the input parameters.
*
Expand Down Expand Up @@ -546,6 +579,9 @@ private void validate() throws IllegalArgumentException {
if (Objects.isNull(cuvsDistanceType)) {
throw new IllegalArgumentException("cuvsDistanceType cannot be null.");
}
if (Objects.isNull(hnswHeuristicType)) {
throw new IllegalArgumentException("hnswHeuristicType cannot be null.");
}
if (nnDescentNumIterations < MIN_NN_DESCENT_NUM_ITERATIONS
|| nnDescentNumIterations > MAX_NN_DESCENT_NUM_ITERATIONS) {
throw new IllegalArgumentException(
Expand Down Expand Up @@ -583,7 +619,8 @@ public AcceleratedHNSWParams build() {
mergeExec,
strategy,
cuvsDistanceType,
nnDescentNumIterations);
nnDescentNumIterations,
hnswHeuristicType);
}
}
}
19 changes: 10 additions & 9 deletions src/main/java/com/nvidia/cuvs/lucene/AcceleratedHNSWUtils.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved.
* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/

Expand Down Expand Up @@ -74,7 +74,8 @@ public static GPUBuiltHnswGraph createSingleVectorHnswGraph(int size, int dimens

/**
* Creates a multi-layer HNSW graph with dynamic number of layers.
* M = cagraGraphDegree/2
* M = ceil(cagraGraphDegree / 2), where cagraGraphDegree is the CAGRA adjacency list's degree
* (its column count). Ceil is used to accommodate odd graph degrees.
* Each layer contains 1/M nodes from the previous layer
* Creates layers until the highest layer has ≤ M nodes
*/
Expand All @@ -85,13 +86,11 @@ public static GPUBuiltHnswGraph createMultiLayerHnswGraph(
CuVSMatrix adjacencyListMatrix,
List<?> vectors,
int hnswLayers,
int graphDegree,
CagraIndexParams params,
QuantizationType quantization)
throws Throwable {

// Calculate M as cagraGraphDegree/2
int M = graphDegree / 2;
int M = Math.ceilDiv((int) adjacencyListMatrix.columns(), 2);

// Store all layers data
List<int[]> layerNodes = new ArrayList<>();
Expand Down Expand Up @@ -309,8 +308,7 @@ public static void writeMeta(
long vectorIndexLength,
int count,
HnswGraph graph,
int[][] graphLevelNodeOffsets,
int graphDegree)
int[][] graphLevelNodeOffsets)
throws IOException {

meta.writeInt(field.number);
Expand All @@ -320,7 +318,10 @@ public static void writeMeta(
meta.writeVLong(vectorIndexLength);
meta.writeVInt(field.getVectorDimension());
meta.writeInt(count);
meta.writeVInt(graphDegree / 2); // M = cagraGraphDegree/2
// M = ceil(cagraGraphDegree / 2), derived from the graph being written rather than from a
// caller-supplied degree: graph.maxConn() is the widest layer-0 adjacency row, which is the
// degree cuVS actually built (it may truncate the requested one for small datasets).
meta.writeVInt(graph == null ? 0 : Math.ceilDiv(graph.maxConn(), 2));

// write graph nodes on each level
if (graph == null) {
Expand Down Expand Up @@ -394,7 +395,7 @@ public static void printInfoStream(InfoStream infoStream, String component, Stri
* @throws IOException I/O Exceptions
*/
public static void writeEmpty(FieldInfo fieldInfo, IndexOutput op) throws IOException {
writeMeta(null, op, fieldInfo, 0, 0, 0, null, null, 0);
writeMeta(null, op, fieldInfo, 0, 0, 0, null, null);
}

/**
Expand Down
Loading