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
78 changes: 31 additions & 47 deletions python/cuml/cuml/neighbors/kernel_density.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@

from cuml.internals.array import CumlArray
from cuml.internals.base import Base
from cuml.internals.input_utils import input_to_cuml_array, input_to_cupy_array
from cuml.internals.interop import InteropMixin, UnsupportedOnGPU
from cuml.internals.outputs import reflect, run_in_internal_context
from cuml.internals.validation import (
check_features,
check_inputs,
check_is_fitted,
check_non_negative,
check_random_seed,
)
from cuml.metrics import pairwise_distances
Expand Down Expand Up @@ -267,7 +267,7 @@ def __init__(
self.metric = metric
self.metric_params = metric_params

@reflect(reset=True)
@reflect(reset="type")
def fit(
self, X, y=None, sample_weight=None, *, convert_dtype=True
) -> "KernelDensity":
Expand All @@ -288,55 +288,40 @@ def fit(
self
Returns the instance itself.
"""
if self.kernel not in VALID_KERNELS:
raise ValueError(f"kernel={self.kernel!r} is not supported")

if isinstance(self.bandwidth, str):
if self.bandwidth == "scott":
self.bandwidth_ = X.shape[0] ** (-1 / (X.shape[1] + 4))
elif self.bandwidth == "silverman":
self.bandwidth_ = (X.shape[0] * (X.shape[1] + 2) / 4) ** (
-1 / (X.shape[1] + 4)
)
else:
if self.bandwidth not in ("scott", "silverman"):
raise ValueError(
f"Expected bandwidth in ['scott', 'silverman'], got {self.bandwidth!r}"
)
elif self.bandwidth <= 0:
raise ValueError(f"Expected bandwidth > 0, got {self.bandwidth}")
else:
self.bandwidth_ = self.bandwidth

if self.kernel not in VALID_KERNELS:
raise ValueError(f"kernel={self.kernel!r} is not supported")

self._X, n_rows, n_cols, _ = input_to_cupy_array(
self._X, self._sample_weight = check_inputs(
self,
X,
sample_weight=sample_weight,
dtype=("float32", "float64"),
convert_dtype=convert_dtype,
order="C",
convert_to_dtype=(np.float32 if convert_dtype else None),
check_dtype=[cp.float32, cp.float64],
reset=True,
)
if self._sample_weight is not None:
check_non_negative(self._sample_weight, input_name="sample_weight")

if n_rows < 1:
raise ValueError(
f"Found array with 0 sample(s) (shape={self._X.shape}) while "
f"a minimum of 1 is required by KernelDensity"
)
if n_cols < 1:
raise ValueError(
f"Found array with 0 feature(s) (shape={self._X.shape}) while "
f"a minimum of 1 is required by KernelDensity"
)

if sample_weight is not None:
self._sample_weight = input_to_cupy_array(
sample_weight,
convert_to_dtype=(np.float32 if convert_dtype else None),
check_dtype=[cp.float32, cp.float64],
check_cols=1,
check_rows=self._X.shape[0],
).array
if self._sample_weight.min() < 0:
raise ValueError("sample_weight must have positive values")
if isinstance(self.bandwidth, str):
if self.bandwidth == "scott":
self.bandwidth_ = self._X.shape[0] ** (
-1 / (self._X.shape[1] + 4)
)
else: # silverman
self.bandwidth_ = (
self._X.shape[0] * (self._X.shape[1] + 2) / 4
) ** (-1 / (self._X.shape[1] + 4))
else:
self._sample_weight = None
self.bandwidth_ = self.bandwidth

return self

Expand All @@ -358,14 +343,13 @@ def score_samples(self, X, *, convert_dtype=True) -> CumlArray:
data.
"""
check_is_fitted(self)
check_features(self, X)

X = input_to_cuml_array(
X = check_inputs(
self,
X,
convert_to_dtype=(self._X.dtype if convert_dtype else None),
check_dtype=[self._X.dtype],
check_cols=self.n_features_in_,
).array
dtype=[self._X.dtype],
convert_dtype=convert_dtype,
order="C",
)
Comment thread
viclafargue marked this conversation as resolved.
if self.metric_params:
if len(self.metric_params) != 1:
raise ValueError(
Expand Down
57 changes: 20 additions & 37 deletions python/cuml/cuml/neighbors/kneighbors_classifier.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import cupy as cp
import numpy as np

import cuml
from cuml.common import input_to_cuml_array
from cuml.common.classification import decode_labels
from cuml.common.doc_utils import generate_docstring
from cuml.internals import get_handle
Expand Down Expand Up @@ -140,14 +139,14 @@ class KNeighborsClassifier(ClassifierMixin, FMajorInputTagMixin, NeighborsBase):
def _attrs_from_cpu(self, model):
return {
"classes_": model.classes_,
"_y": cp.asarray(model._y, order="F", dtype=np.int32),
"_y": cp.asarray(model._y, dtype=np.int32, order="F"),
**super()._attrs_from_cpu(model),
}

def _attrs_to_cpu(self, model):
return {
"classes_": self.classes_,
"_y": self._y.get(),
"_y": cp.asnumpy(self._y),
"outputs_2d_": self.outputs_2d_,
**super()._attrs_to_cpu(model),
}
Expand All @@ -164,7 +163,7 @@ class KNeighborsClassifier(ClassifierMixin, FMajorInputTagMixin, NeighborsBase):
self.weights = weights

@generate_docstring(convert_dtype_cast='np.float32')
@reflect(reset=True)
@reflect(reset="type")
def fit(self, X, y, *, convert_dtype=True) -> "KNeighborsClassifier":
"""
Fit a GPU index for k-nearest neighbors classifier model.
Expand All @@ -178,12 +177,13 @@ class KNeighborsClassifier(ClassifierMixin, FMajorInputTagMixin, NeighborsBase):
super().fit(X, convert_dtype=convert_dtype)
y, classes = check_y(
y,
dtype="int32",
convert_dtype=convert_dtype,
order="F",
dtype=np.int32,
accept_multi_output=True,
return_classes=True,
)
check_consistent_length(X, y)
check_consistent_length(self._fit_X, y)
self.classes_ = classes
self._y = y
return self
Expand All @@ -210,20 +210,11 @@ class KNeighborsClassifier(ClassifierMixin, FMajorInputTagMixin, NeighborsBase):
X, return_distance=True, convert_dtype=convert_dtype
)

cdef size_t n_rows
inds, n_rows, _, _ = input_to_cuml_array(
knn_indices,
order='C',
check_dtype=np.int64,
convert_to_dtype=(np.int64 if convert_dtype else None),
)

dists, _, _, _ = input_to_cuml_array(
knn_distances,
order='C',
check_dtype=np.float32,
convert_to_dtype=(np.float32 if convert_dtype else None),
inds_cp = cp.ascontiguousarray(
knn_indices.to_output("cupy"), dtype=np.int64
)
dists_cp = knn_distances.to_output("cupy")
cdef size_t n_rows = inds_cp.shape[0]

# Allocate array for predictions
out_cols = self._y.shape[1] if self._y.ndim == 2 else 1
Expand All @@ -238,14 +229,14 @@ class KNeighborsClassifier(ClassifierMixin, FMajorInputTagMixin, NeighborsBase):
y_vec.push_back(<int*><uintptr_t>col.data.ptr)

# Compute weights (returns None for uniform weights)
weights_cp = compute_weights(dists.to_output('cupy'), self.weights)
weights_cp = compute_weights(dists_cp, self.weights)
cdef float* weights_ptr = <float*><uintptr_t>(
0 if weights_cp is None else weights_cp.data.ptr
)

handle = get_handle()
cdef handle_t* handle_ = <handle_t*><size_t>handle.getHandle()
cdef int64_t* inds_ptr = <int64_t*><uintptr_t>inds.ptr
cdef int64_t* inds_ptr = <int64_t*><uintptr_t>inds_cp.data.ptr
cdef size_t n_samples_fit = self._y.shape[0]
cdef int n_neighbors = self.n_neighbors
with nogil:
Expand Down Expand Up @@ -283,20 +274,12 @@ class KNeighborsClassifier(ClassifierMixin, FMajorInputTagMixin, NeighborsBase):
X, return_distance=True, convert_dtype=convert_dtype
)

cdef size_t n_rows
inds, n_rows, _, _ = input_to_cuml_array(
knn_indices,
order='C',
check_dtype=np.int64,
convert_to_dtype=(np.int64 if convert_dtype else None)
)

dists, _, _, _ = input_to_cuml_array(
knn_distances,
order='C',
check_dtype=np.float32,
convert_to_dtype=(np.float32 if convert_dtype else None)
inds_cp = cp.ascontiguousarray(
knn_indices.to_output("cupy"), dtype=np.int64
)
dists_cp = knn_distances.to_output("cupy")
cdef size_t n_rows = inds_cp.shape[0]
index = knn_indices.index

if self._y.ndim == 1 or self._y.shape[1] == 1:
n_classes = [len(self.classes_)]
Expand All @@ -311,21 +294,21 @@ class KNeighborsClassifier(ClassifierMixin, FMajorInputTagMixin, NeighborsBase):
cdef vector[int*] y_vec
for n, y in zip(n_classes, ys):
proba = CumlArray.zeros(
(n_rows, n), dtype=np.float32, order="C", index=inds.index
(n_rows, n), dtype=np.float32, order="C", index=index
)
probas.append(proba)
out_vec.push_back(<float*><uintptr_t>proba.ptr)
y_vec.push_back(<int*><uintptr_t>y.data.ptr)

# Compute weights (returns None for uniform weights)
weights_cp = compute_weights(dists.to_output('cupy'), self.weights)
weights_cp = compute_weights(dists_cp, self.weights)
cdef float* weights_ptr = <float*><uintptr_t>(
0 if weights_cp is None else weights_cp.data.ptr
)

handle = get_handle()
cdef handle_t* handle_ = <handle_t*><size_t>handle.getHandle()
cdef int64_t* inds_ptr = <int64_t*><uintptr_t>inds.ptr
cdef int64_t* inds_ptr = <int64_t*><uintptr_t>inds_cp.data.ptr
cdef size_t n_samples_fit = self._y.shape[0]
cdef int n_neighbors = self.n_neighbors
with nogil:
Expand Down
51 changes: 22 additions & 29 deletions python/cuml/cuml/neighbors/kneighbors_regressor.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@
# SPDX-FileCopyrightText: Copyright (c) 2019-2026, NVIDIA CORPORATION.
# SPDX-License-Identifier: Apache-2.0
#
import cupy as cp
import numpy as np

from cuml.common import input_to_cuml_array
from cuml.common.doc_utils import generate_docstring
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.interop import UnsupportedOnGPU
from cuml.internals.mixins import FMajorInputTagMixin, RegressorMixin
from cuml.internals.validation import check_consistent_length, check_y
from cuml.neighbors.nearest_neighbors import NeighborsBase
from cuml.neighbors.weights import compute_weights

Expand Down Expand Up @@ -142,13 +143,13 @@ class KNeighborsRegressor(RegressorMixin, FMajorInputTagMixin, NeighborsBase):

def _attrs_from_cpu(self, model):
return {
"_y": to_gpu(model._y, order="F", dtype=np.float32),
"_y": cp.asarray(model._y, dtype=np.float32, order="F"),
**super()._attrs_from_cpu(model),
}

def _attrs_to_cpu(self, model):
return {
"_y": to_cpu(self._y),
"_y": cp.asnumpy(self._y),
**super()._attrs_to_cpu(model),
}

Expand All @@ -164,7 +165,7 @@ class KNeighborsRegressor(RegressorMixin, FMajorInputTagMixin, NeighborsBase):
self.weights = weights

@generate_docstring(convert_dtype_cast='np.float32')
@reflect(reset=True)
@reflect(reset="type")
def fit(self, X, y, *, convert_dtype=True) -> "KNeighborsRegressor":
"""
Fit a GPU index for k-nearest neighbors regression model.
Expand All @@ -176,13 +177,15 @@ class KNeighborsRegressor(RegressorMixin, FMajorInputTagMixin, NeighborsBase):
)
super().fit(X, convert_dtype=convert_dtype)

self._y = input_to_cuml_array(
y = check_y(
y,
order='F',
check_rows=self.n_samples_fit_,
check_dtype=np.float32,
convert_to_dtype=(np.float32 if convert_dtype else None),
).array
dtype="float32",
convert_dtype=convert_dtype,
order="F",
accept_multi_output=True,
)
check_consistent_length(self._fit_X, y)
self._y = y

return self

Expand All @@ -203,28 +206,18 @@ class KNeighborsRegressor(RegressorMixin, FMajorInputTagMixin, NeighborsBase):
X, return_distance=True, convert_dtype=convert_dtype
)

cdef size_t n_rows
inds, n_rows, _, _ = input_to_cuml_array(
knn_indices,
order='C',
check_dtype=np.int64,
convert_to_dtype=(np.int64 if convert_dtype else None),
inds_cp = cp.ascontiguousarray(
knn_indices.to_output("cupy"), dtype=np.int64
)

dists = input_to_cuml_array(
knn_distances,
order='C',
check_dtype=np.float32,
convert_to_dtype=(np.float32 if convert_dtype else None),
).array

cdef int64_t* inds_ctype = <int64_t*><uintptr_t>inds.ptr
dists_cp = knn_distances.to_output("cupy")
cdef size_t n_rows = inds_cp.shape[0]
cdef int64_t* inds_ctype = <int64_t*><uintptr_t>inds_cp.data.ptr

res_cols = 1 if self._y.ndim == 1 else self._y.shape[1]
res_shape = n_rows if res_cols == 1 else (n_rows, res_cols)

out = CumlArray.zeros(
res_shape, dtype=np.float32, order="C", index=inds.index
res_shape, dtype=np.float32, order="C", index=knn_indices.index
)

cdef float* out_ptr = <float*><uintptr_t>out.ptr
Expand All @@ -233,14 +226,14 @@ class KNeighborsRegressor(RegressorMixin, FMajorInputTagMixin, NeighborsBase):
cdef float* y_ptr
for col_num in range(res_cols):
col = self._y if res_cols == 1 else self._y[:, col_num]
y_ptr = <float*><uintptr_t>col.ptr
y_ptr = <float*><uintptr_t>col.data.ptr
y_vec.push_back(y_ptr)

handle = get_handle()
cdef handle_t* handle_ = <handle_t*><size_t>handle.getHandle()

# Compute weights (returns None for uniform weights)
weights_cp = compute_weights(dists.to_output('cupy'), self.weights)
weights_cp = compute_weights(dists_cp, self.weights)
cdef float* weights_ctype = <float*><uintptr_t>(
0 if weights_cp is None else weights_cp.data.ptr
)
Expand Down
Loading
Loading