Skip to content
Merged
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
34 changes: 33 additions & 1 deletion cpp/include/cuml/neighbors/knn.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: Copyright (c) 2019-2025, NVIDIA CORPORATION.
* SPDX-FileCopyrightText: Copyright (c) 2019-2026, NVIDIA CORPORATION.
* SPDX-License-Identifier: Apache-2.0
*/

Expand Down Expand Up @@ -71,6 +71,38 @@ void rbc_knn_query(const raft::handle_t& handle,
int64_t* out_inds,
float* out_dists);

/**
* @brief Perform a radius neighbors query on the fit index.
*
* A single query requires two calls to this API:
* - The first call should pass adj_indices=nullptr and nnz=0. This will
* fill in adj_indptr, letting you get the size needed for adj_indices.
* - The second call should pass adj_indices and nnz, an array of size
* nnz=adj_indptr[-1]. This will fill in adj_indices.
*
Comment thread
jcrist marked this conversation as resolved.
* @param[in] handle: RAFT handle
* @param[in] rbc_index: the fit RBC index
* @param[in] query: the query points as a C-contiguous array
* @param[in] n_query: number of rows in the query
* @param[in] dim: number of columns in the query
* @param[in] radius: the neighborhood radius
* @param[out] adj_indptr: the indptr array in output CSR adjacency matrix,
* of shape n_query + 1.
* @param[out] adj_indices: the indices array in the output CSR adjacency
* matrix, of shape adj_indptr[-1]. Should be NULL on the first
* call.
* @param[in] nnz: the number of elements in adj_indices, or 0 on the first call.
*/
void rbc_radius_neighbors_graph(const raft::handle_t& handle,
const std::uintptr_t& rbc_index,
const float* query,
int64_t n_query,
int64_t dim,
float radius,
int64_t* adj_indptr,
int64_t* adj_indices = nullptr,
int64_t nnz = 0);

/**
* @brief Free the RBC index
*
Expand Down
24 changes: 23 additions & 1 deletion cpp/src/knn/knn.cu
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: Copyright (c) 2019-2025, NVIDIA CORPORATION.
* SPDX-FileCopyrightText: Copyright (c) 2019-2026, NVIDIA CORPORATION.
* SPDX-License-Identifier: Apache-2.0
*/

Expand Down Expand Up @@ -200,6 +200,28 @@ void rbc_knn_query(const raft::handle_t& handle,
handle, *rbc_index_ptr, query_view, indices_view, distances_view, k);
}

void rbc_radius_neighbors_graph(const raft::handle_t& handle,
const std::uintptr_t& rbc_index,
const float* query,
int64_t n_query,
int64_t dim,
float radius,
int64_t* adj_indptr,
int64_t* adj_indices,
int64_t nnz)
{
auto index_ptr = reinterpret_cast<cuvs::neighbors::ball_cover::index<int64_t, float>*>(rbc_index);

cuvs::neighbors::ball_cover::eps_nn(
handle,
*index_ptr,
raft::make_device_vector_view<int64_t, int64_t>(adj_indptr, n_query + 1),
raft::make_device_vector_view<int64_t, int64_t>(adj_indices, nnz),
raft::make_device_vector_view<int64_t, int64_t>(nullptr, 0),
raft::make_device_matrix_view<const float, int64_t>(query, n_query, dim),
radius);
}

void rbc_free_index(std::uintptr_t rbc_index)
{
// Cast back to the original type and delete
Expand Down
19 changes: 18 additions & 1 deletion python/cuml/cuml/accel/_wrappers/sklearn/neighbors.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
#
# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION.
# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION.
# SPDX-License-Identifier: Apache-2.0
#

import cuml.neighbors
from cuml.accel.estimator_proxy import ProxyBase
from cuml.common.sparse_utils import is_sparse
from cuml.internals.interop import UnsupportedOnGPU

__all__ = (
"NearestNeighbors",
Expand All @@ -18,6 +20,21 @@ class NearestNeighbors(ProxyBase):
_gpu_class = cuml.neighbors.NearestNeighbors
_other_attributes = frozenset(("_fit_method", "_tree", "_fit_X"))

def _gpu_radius_neighbors_graph(
self, X=None, radius=None, mode="connectivity", sort_results=False
):
if mode != "connectivity":
raise UnsupportedOnGPU(f"`mode={mode!r}` is not supported")
if sort_results:
raise UnsupportedOnGPU("`sort_results=True` is not supported")
if is_sparse(X):
raise UnsupportedOnGPU("Sparse inputs are not supported")
if self.effective_metric_ not in ["l2", "euclidean"]:
raise UnsupportedOnGPU(
f"metric={self.effective_metric_!r} is not supported"
)
return self._gpu.radius_neighbors_graph(X=X, radius=radius)
Comment thread
jcrist marked this conversation as resolved.


class KNeighborsClassifier(ProxyBase):
_gpu_class = cuml.neighbors.KNeighborsClassifier
Expand Down
6 changes: 2 additions & 4 deletions python/cuml/cuml/neighbors/kneighbors_classifier.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ from cuml.internals.array import CumlArray
from cuml.internals.interop import UnsupportedOnGPU
from cuml.internals.mixins import ClassifierMixin, FMajorInputTagMixin
from cuml.internals.outputs import reflect, run_in_internal_context
from cuml.neighbors.nearest_neighbors import NearestNeighbors
from cuml.neighbors.nearest_neighbors import NeighborsBase
from cuml.neighbors.weights import compute_weights

from libc.stdint cimport int64_t, uintptr_t
Expand Down Expand Up @@ -49,9 +49,7 @@ cdef extern from "cuml/neighbors/knn.hpp" namespace "ML" nogil:
) except +


class KNeighborsClassifier(ClassifierMixin,
FMajorInputTagMixin,
NearestNeighbors):
class KNeighborsClassifier(ClassifierMixin, FMajorInputTagMixin, NeighborsBase):
"""
K-Nearest Neighbors Classifier is an instance-based learning technique,
that keeps training samples around for prediction, rather than trying
Expand Down
4 changes: 2 additions & 2 deletions python/cuml/cuml/neighbors/kneighbors_regressor.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ from cuml.internals import get_handle, reflect
from cuml.internals.array import CumlArray
from cuml.internals.interop import UnsupportedOnGPU, to_cpu, to_gpu
from cuml.internals.mixins import FMajorInputTagMixin, RegressorMixin
from cuml.neighbors.nearest_neighbors import NearestNeighbors
from cuml.neighbors.nearest_neighbors import NeighborsBase
from cuml.neighbors.weights import compute_weights

from libc.stdint cimport int64_t, uintptr_t
Expand All @@ -32,7 +32,7 @@ cdef extern from "cuml/neighbors/knn.hpp" namespace "ML" nogil:
) except +


class KNeighborsRegressor(RegressorMixin, FMajorInputTagMixin, NearestNeighbors):
class KNeighborsRegressor(RegressorMixin, FMajorInputTagMixin, NeighborsBase):
"""
K-Nearest Neighbors Regressor is an instance-based learning technique,
that keeps training samples around for prediction, rather than trying
Expand Down
Loading