From e11bd33589f8a9026fc074034499d051f2eefcef Mon Sep 17 00:00:00 2001 From: Jim Crist-Harif Date: Tue, 16 Dec 2025 11:44:21 -0600 Subject: [PATCH 01/21] Initial deprecation infra --- python/cuml/cuml/internals/__init__.py | 2 +- python/cuml/cuml/internals/base.py | 90 +++++++++++++++++++++++++- 2 files changed, 88 insertions(+), 4 deletions(-) diff --git a/python/cuml/cuml/internals/__init__.py b/python/cuml/cuml/internals/__init__.py index 26400d2f41..07b4cdc896 100644 --- a/python/cuml/cuml/internals/__init__.py +++ b/python/cuml/cuml/internals/__init__.py @@ -4,7 +4,7 @@ # # TODO: remove in 26.04 import cuml.internals.memory_utils -from cuml.internals.base import Base +from cuml.internals.base import Base, get_handle from cuml.internals.internals import GraphBasedDimRedCallback from cuml.internals.outputs import ( exit_internal_context, diff --git a/python/cuml/cuml/internals/base.py b/python/cuml/cuml/internals/base.py index 4cd51b7a05..a31cc9634d 100644 --- a/python/cuml/cuml/internals/base.py +++ b/python/cuml/cuml/internals/base.py @@ -4,6 +4,8 @@ # import inspect import os +import threading +import warnings import pylibraft.common.handle @@ -17,6 +19,88 @@ from cuml.internals.mixins import TagsMixin from cuml.internals.outputs import check_output_type +_THREAD_STATE = threading.local() + + +class DeprecatedHandleDescriptor: + """A descriptor to ease deprecating the `handle` parameter.""" + + def __set__(self, obj, value): + if value is not None: + params = obj._get_param_names() if isinstance(obj, Base) else [] + if "n_streams" in params: + suffix = ( + " To configure the number of streams used, please use the " + "`n_streams` parameter instead." + ) + elif "device_ids" in params: + suffix = ( + " To configure multi-device execution, please use the " + "`device_ids` parameter instead." + ) + else: + suffix = "" + warnings.warn( + f"The `handle` argument to `{type(obj).__name__}` was deprecated " + f"in 26.02 and will be removed in 26.04. There is no need to " + f"manually specify a `handle`, cuml now manages this resource " + f"for you automatically.{suffix}", + FutureWarning, + ) + obj.__dict__["handle"] = value + + +def get_handle(*, handle=None, model=None, n_streams=0, device_ids=None): + """Get a `pylibraft.common.Handle`. + + Parameters + ---------- + handle : pylibraft.common.Handle or None, optional + A `handle` argument to a function. Will raise a nice deprecation + warning and return if provided. Will be removed once the deprecation of + `handle` arguments is complete. + model : cuml.Base or None, optional + A model to extract the handle from (if one is configured). Will be removed + once the deprecation of `handle` arguments is complete. + n_streams : int, default=0 + The number of streams to use for a backing stream pool. If non-zero + a temporary `Handle` with a pool that size will be created. Otherwise + the default threadlocal `Handle` will be used. + device_ids : list[int], "all", or None, default=None + If non-None, will return a `pylibraft.common.DeviceResourcesSNMG`, + enabling multi-device execution. May be a list of device IDs, or + ``"all"`` to use all available devices. + """ + if handle is not None: + warnings.warn( + ( + "The `handle` argument was deprecated in 26.02 and will be " + "removed in 26.04. There is no need to manually specify a " + "`handle`, cuml manages this resource for you automatically." + ), + FutureWarning, + ) + return handle + + if model is not None and model.handle is not None: + # Deprecation of model.handle is handled separately by the descriptor + return model.handle + + if n_streams == 0 and device_ids is None: + if not hasattr(_THREAD_STATE, "handle"): + _THREAD_STATE.handle = pylibraft.common.handle.Handle() + return _THREAD_STATE.handle + elif device_ids is not None: + if n_streams != 0: + raise ValueError( + "Cannot specify both `device_ids` and `n_streams`" + ) + return pylibraft.common.handle.DeviceResourcesSNMG( + device_ids=(None if device_ids == "all" else device_ids) + ) + else: + return pylibraft.common.handle.Handle(n_streams=n_streams) + class Base(TagsMixin): """Base class for cuml estimators. @@ -86,6 +170,8 @@ def predict(self, X): return cp.ones(len(X), dtype="int32") """ + handle = DeprecatedHandleDescriptor() + def __init__( self, *, @@ -93,9 +179,7 @@ def __init__( verbose=False, output_type=None, ): - self.handle = ( - pylibraft.common.handle.Handle() if handle is None else handle - ) + self.handle = handle self.verbose = verbose if output_type is None: output_type = cuml.global_settings.output_type or "input" From a94853229e539903af5bb8d4ff927aecf025448d Mon Sep 17 00:00:00 2001 From: Jim Crist-Harif Date: Tue, 16 Dec 2025 16:06:16 -0600 Subject: [PATCH 02/21] Use `get_handle` in `cuml.cluster` --- python/cuml/cuml/cluster/agglomerative.pyx | 7 +- python/cuml/cuml/cluster/dbscan.pyx | 8 +- python/cuml/cuml/cluster/hdbscan/hdbscan.pyx | 86 +++++++++++-------- python/cuml/cuml/cluster/kmeans.pyx | 17 ++-- .../cuml/cuml/cluster/spectral_clustering.pyx | 6 +- python/cuml/tests/test_dbscan.py | 13 +-- python/cuml/tests/test_hdbscan.py | 11 +-- 7 files changed, 74 insertions(+), 74 deletions(-) diff --git a/python/cuml/cuml/cluster/agglomerative.pyx b/python/cuml/cuml/cluster/agglomerative.pyx index c9d41fa9da..8392d08624 100644 --- a/python/cuml/cuml/cluster/agglomerative.pyx +++ b/python/cuml/cuml/cluster/agglomerative.pyx @@ -8,7 +8,7 @@ from cuml.common import input_to_cuml_array from cuml.common.array_descriptor import CumlArrayDescriptor from cuml.common.doc_utils import generate_docstring from cuml.internals.array import CumlArray -from cuml.internals.base import Base +from cuml.internals.base import Base, get_handle from cuml.internals.mixins import ClusterMixin, CMajorInputTagMixin from cuml.internals.outputs import reflect @@ -194,7 +194,8 @@ class AgglomerativeClustering(Base, ClusterMixin, CMajorInputTagMixin): labels = CumlArray.empty(n_rows, dtype="int32", order="C") children = CumlArray.empty((n_rows - 1, 2), dtype="int32", order="C") - cdef handle_t* handle_ = self.handle.getHandle() + handle = get_handle(model=self) + cdef handle_t* handle_ = handle.getHandle() cdef int c = self.c cdef float* X_ptr = X.ptr cdef int* children_ptr = children.ptr @@ -214,7 +215,7 @@ class AgglomerativeClustering(Base, ClusterMixin, CMajorInputTagMixin): use_knn, c, ) - self.handle.sync() + handle.sync() # We only support single linkage for now, for other linkage types # n_connected_components_ and n_leaves_ will differ diff --git a/python/cuml/cuml/cluster/dbscan.pyx b/python/cuml/cuml/cluster/dbscan.pyx index 680a2f130f..d802c3c7d6 100644 --- a/python/cuml/cuml/cluster/dbscan.pyx +++ b/python/cuml/cuml/cluster/dbscan.pyx @@ -2,7 +2,6 @@ # SPDX-FileCopyrightText: Copyright (c) 2019-2025, NVIDIA CORPORATION. # SPDX-License-Identifier: Apache-2.0 # - import cupy as cp import numpy as np @@ -10,7 +9,7 @@ from cuml.common.array_descriptor import CumlArrayDescriptor from cuml.common.doc_utils import generate_docstring from cuml.internals import logger, reflect from cuml.internals.array import CumlArray -from cuml.internals.base import Base +from cuml.internals.base import Base, get_handle from cuml.internals.input_utils import input_to_cuml_array from cuml.internals.interop import ( InteropMixin, @@ -399,7 +398,8 @@ class DBSCAN(Base, cdef uintptr_t core_sample_indices_ptr = ( 0 if core_sample_indices is None else core_sample_indices.ptr ) - cdef handle_t* handle_ = self.handle.getHandle() + handle = get_handle(model=self) + cdef handle_t* handle_ = handle.getHandle() cdef bool X_f32 = X.dtype == np.float32 cdef bool labels_i32 = labels.dtype == np.int32 @@ -467,7 +467,7 @@ class DBSCAN(Base, algorithm, verbose, multi_gpu) - self.handle.sync() + handle.sync() if core_sample_indices is not None: # Trim the core_sample_indices array if necessary. In the common case diff --git a/python/cuml/cuml/cluster/hdbscan/hdbscan.pyx b/python/cuml/cuml/cluster/hdbscan/hdbscan.pyx index a603bee043..65d3cdbf1e 100644 --- a/python/cuml/cuml/cluster/hdbscan/hdbscan.pyx +++ b/python/cuml/cuml/cluster/hdbscan/hdbscan.pyx @@ -2,7 +2,6 @@ # SPDX-License-Identifier: Apache-2.0 import cupy as cp import numpy as np -from pylibraft.common.handle import Handle import cuml from cuml.common import input_to_cuml_array @@ -10,7 +9,7 @@ from cuml.common.array_descriptor import CumlArrayDescriptor from cuml.common.doc_utils import generate_docstring from cuml.internals import logger, reflect from cuml.internals.array import CumlArray -from cuml.internals.base import Base +from cuml.internals.base import Base, get_handle from cuml.internals.interop import ( InteropMixin, UnsupportedOnGPU, @@ -273,7 +272,7 @@ cdef class _HDBSCANState: cdef size_t n_leaves = dendrogram.shape[0] + 1 - handle = Handle() + handle = get_handle() cdef handle_t *handle_ = handle.getHandle() self.condensed_tree = new lib.CondensedHierarchy[int64_t, float](handle_[0], n_leaves) @@ -642,6 +641,12 @@ class HDBSCAN(Base, InteropMixin, ClusterMixin, CMajorInputTagMixin): memory usage. This is independent from knn_overlap_factor as long as 'knn_overlap_factor' < 'knn_n_clusters'. + device_ids : list[int], "all", or None, default=None + The device IDs to use during fitting (only used when + `build_algo=nn_descent` and `nnd_n_clusters > 1`). May be a list of + ids, ``"all"`` (to use all available devices), or ``None`` (to fit + using a single GPU only). Default is None. + Attributes ---------- labels_ : ndarray, shape (n_samples, ) @@ -698,7 +703,8 @@ class HDBSCAN(Base, InteropMixin, ClusterMixin, CMajorInputTagMixin): "gen_min_span_tree", "prediction_data", "build_algo", - "build_kwds" + "build_kwds", + "device_ids", ] @classmethod @@ -759,9 +765,10 @@ class HDBSCAN(Base, InteropMixin, ClusterMixin, CMajorInputTagMixin): raw_data = to_gpu(raw_data_cpu, order="C", dtype="float32") labels = to_gpu(model.labels_, order="C", dtype="int64") - state = _HDBSCANState.from_sklearn(self.handle, model, raw_data) + handle = get_handle(model=self) + state = _HDBSCANState.from_sklearn(handle, model, raw_data) if model._prediction_data is not None: - state.generate_prediction_data(self.handle, raw_data, labels) + state.generate_prediction_data(handle, raw_data, labels) return { # XXX: `hdbscan.HDBSCAN` doesn't set `n_features_in_` currently, we need @@ -796,24 +803,27 @@ class HDBSCAN(Base, InteropMixin, ClusterMixin, CMajorInputTagMixin): out["_prediction_data"] = self.prediction_data_ return out - def __init__(self, *, - min_cluster_size=5, - min_samples=None, - cluster_selection_epsilon=0.0, - max_cluster_size=0, - metric='euclidean', - alpha=1.0, - p=None, - cluster_selection_method='eom', - allow_single_cluster=False, - gen_min_span_tree=False, - handle=None, - verbose=False, - output_type=None, - prediction_data=False, - build_algo='brute_force', - build_kwds=None): - + def __init__( + self, + *, + min_cluster_size=5, + min_samples=None, + cluster_selection_epsilon=0.0, + max_cluster_size=0, + metric='euclidean', + alpha=1.0, + p=None, + cluster_selection_method='eom', + allow_single_cluster=False, + gen_min_span_tree=False, + handle=None, + verbose=False, + output_type=None, + prediction_data=False, + build_algo='brute_force', + build_kwds=None, + device_ids=None, + ): super().__init__(handle=handle, verbose=verbose, output_type=output_type) self.min_cluster_size = min_cluster_size self.min_samples = min_samples @@ -828,6 +838,7 @@ class HDBSCAN(Base, InteropMixin, ClusterMixin, CMajorInputTagMixin): self.prediction_data = prediction_data self.build_algo = build_algo self.build_kwds = build_kwds + self.device_ids = device_ids self._single_linkage_tree = None self._min_spanning_tree = None @@ -906,7 +917,8 @@ class HDBSCAN(Base, InteropMixin, ClusterMixin, CMajorInputTagMixin): with cuml.using_output_type("cuml"): labels = self.labels_ - self._state.generate_prediction_data(self.handle, self._raw_data, labels) + handle = get_handle(model=self) + self._state.generate_prediction_data(handle, self._raw_data, labels) self.prediction_data = True @generate_docstring() @@ -1037,7 +1049,7 @@ class HDBSCAN(Base, InteropMixin, ClusterMixin, CMajorInputTagMixin): min_spanning_tree, single_linkage_tree, ) = _HDBSCANState.init_and_fit( - self.handle, + get_handle(model=self, device_ids=self.device_ids), self._raw_data, params, metric, @@ -1081,7 +1093,8 @@ class HDBSCAN(Base, InteropMixin, ClusterMixin, CMajorInputTagMixin): state_dict = state.pop("_state_dict", None) self.__dict__.update(state) if state_dict is not None: - self._state = _HDBSCANState.from_dict(self.handle, state_dict) + handle = get_handle(model=self) + self._state = _HDBSCANState.from_dict(handle, state_dict) if self.prediction_data: self.generate_prediction_data() @@ -1165,7 +1178,8 @@ def all_points_membership_vectors(clusterer, int batch_size=4096): cdef float* X_ptr = clusterer._raw_data.ptr cdef float* membership_vec_ptr = membership_vec.ptr cdef DistanceType metric = _metrics_mapping[clusterer.metric] - cdef handle_t* handle_ = clusterer.handle.getHandle() + handle = get_handle(model=clusterer) + cdef handle_t* handle_ = handle.getHandle() with nogil: lib.compute_all_points_membership_vectors( @@ -1177,7 +1191,7 @@ def all_points_membership_vectors(clusterer, int batch_size=4096): membership_vec_ptr, batch_size ) - clusterer.handle.sync() + handle.sync() return membership_vec @@ -1248,7 +1262,8 @@ def membership_vector(clusterer, points_to_predict, int batch_size=4096, convert cdef float* membership_vec_ptr = membership_vec.ptr cdef int min_samples = clusterer.min_samples or clusterer.min_cluster_size cdef DistanceType metric = _metrics_mapping[clusterer.metric] - cdef handle_t* handle_ = clusterer.handle.getHandle() + handle = get_handle(model=clusterer) + cdef handle_t* handle_ = handle.getHandle() with nogil: lib.compute_membership_vector( @@ -1263,7 +1278,7 @@ def membership_vector(clusterer, points_to_predict, int batch_size=4096, convert membership_vec_ptr, batch_size ) - clusterer.handle.sync() + handle.sync() return membership_vec @@ -1342,7 +1357,8 @@ def approximate_predict(clusterer, points_to_predict, convert_dtype=True): cdef float* prediction_probs_ptr = prediction_probs.ptr cdef DistanceType metric = _metrics_mapping[clusterer.metric] cdef int min_samples = clusterer.min_samples or clusterer.min_cluster_size, - cdef handle_t* handle_ = clusterer.handle.getHandle() + handle = get_handle(model=clusterer) + cdef handle_t* handle_ = handle.getHandle() with nogil: lib.out_of_sample_predict( @@ -1358,7 +1374,7 @@ def approximate_predict(clusterer, points_to_predict, convert_dtype=True): prediction_labels_ptr, prediction_probs_ptr, ) - clusterer.handle.sync() + handle.sync() return prediction_labels, prediction_probs @@ -1393,7 +1409,6 @@ def _condense_hierarchy(dendrogram, min_cluster_size): def _extract_clusters( condensed_tree, - handle=None, allow_single_cluster=False, max_cluster_size=0, cluster_selection_method="eom", @@ -1437,8 +1452,7 @@ def _extract_clusters( "leaf": lib.CLUSTER_SELECTION_METHOD.LEAF, }[cluster_selection_method] - if handle is None: - handle = Handle() + handle = get_handle() cdef handle_t* handle_ = handle.getHandle() lib._extract_clusters( diff --git a/python/cuml/cuml/cluster/kmeans.pyx b/python/cuml/cuml/cluster/kmeans.pyx index 627475cdfb..1a30607a99 100644 --- a/python/cuml/cuml/cluster/kmeans.pyx +++ b/python/cuml/cuml/cluster/kmeans.pyx @@ -10,7 +10,7 @@ from cuml.common import input_to_cuml_array from cuml.common.array_descriptor import CumlArrayDescriptor from cuml.common.doc_utils import generate_docstring from cuml.internals.array import CumlArray -from cuml.internals.base import Base +from cuml.internals.base import Base, get_handle from cuml.internals.interop import ( InteropMixin, UnsupportedOnGPU, @@ -582,12 +582,13 @@ class KMeans(Base, ) # Prepare for libcuml call - cdef handle_t* handle_ = self.handle.getHandle() + handle = get_handle(model=self) + cdef handle_t* handle_ = handle.getHandle() cdef lib.KMeansParams params _kmeans_init_params(self, params) n_iter = _kmeans_fit(handle_[0], params, X_m, sample_weight_m, centers) labels, inertia = _kmeans_predict(handle_[0], params, X_m, sample_weight_m, centers) - self.handle.sync() + handle.sync() # Store fitted attributes and return self.cluster_centers_ = centers @@ -661,14 +662,15 @@ class KMeans(Base, check_cols=1, ) - cdef handle_t* handle_ = self.handle.getHandle() + handle = get_handle(model=self) + cdef handle_t* handle_ = handle.getHandle() cdef lib.KMeansParams params _kmeans_init_params(self, params) labels, inertia = _kmeans_predict( handle_[0], params, X_m, sample_weight_m, self.cluster_centers_ ) - self.handle.sync() + handle.sync() return labels, inertia @generate_docstring(return_values={'name': 'preds', @@ -720,7 +722,8 @@ class KMeans(Base, cdef uintptr_t centers_ptr = self.cluster_centers_.ptr cdef uintptr_t out_ptr = out.ptr - cdef handle_t* handle_ = self.handle.getHandle() + handle = get_handle(model=self) + cdef handle_t* handle_ = handle.getHandle() cdef lib.KMeansParams params _kmeans_init_params(self, params) @@ -770,7 +773,7 @@ class KMeans(Base, n_cols, out_ptr, ) - self.handle.sync() + handle.sync() return out @generate_docstring(return_values={'name': 'score', diff --git a/python/cuml/cuml/cluster/spectral_clustering.pyx b/python/cuml/cuml/cluster/spectral_clustering.pyx index 46ad90391a..cd9831e7c1 100644 --- a/python/cuml/cuml/cluster/spectral_clustering.pyx +++ b/python/cuml/cuml/cluster/spectral_clustering.pyx @@ -8,12 +8,11 @@ import cupy as cp import cupyx.scipy.sparse as cp_sp import numpy as np import scipy.sparse as sp -from pylibraft.common.handle import Handle import cuml from cuml.common.array_descriptor import CumlArrayDescriptor from cuml.internals.array import CumlArray -from cuml.internals.base import Base +from cuml.internals.base import Base, get_handle from cuml.internals.input_utils import input_to_cupy_array from cuml.internals.utils import check_random_seed @@ -138,8 +137,7 @@ def spectral_clustering(X, >>> X = np.random.rand(100, 10).astype(np.float32) >>> labels = spectral_clustering(X, n_clusters=5, n_neighbors=10, random_state=42) """ - if handle is None: - handle = Handle() + handle = get_handle(handle=handle) cdef float* affinity_data_ptr = NULL cdef int* affinity_rows_ptr = NULL diff --git a/python/cuml/tests/test_dbscan.py b/python/cuml/tests/test_dbscan.py index 15bffd4f6c..33baebe513 100644 --- a/python/cuml/tests/test_dbscan.py +++ b/python/cuml/tests/test_dbscan.py @@ -14,7 +14,6 @@ from cuml.testing.utils import ( array_equal, assert_dbscan_equal, - get_handle, quality_param, stress_param, unit_param, @@ -23,7 +22,6 @@ @pytest.mark.parametrize("max_mbytes_per_batch", [1e3, None]) @pytest.mark.parametrize("datatype", [np.float32, np.float64]) -@pytest.mark.parametrize("use_handle", [True, False]) @pytest.mark.parametrize( "nrows", [unit_param(500), quality_param(5000), stress_param(500000)] ) @@ -44,7 +42,6 @@ @pytest.mark.parametrize("algorithm", ["brute", "rbc"]) def test_dbscan( datatype, - use_handle, nrows, ncols, max_mbytes_per_batch, @@ -76,11 +73,8 @@ def test_dbscan( ) X = X.astype(datatype) - handle, stream = get_handle(use_handle) - eps = 1 cuml_dbscan = cuDBSCAN( - handle=handle, eps=eps, min_samples=2, algorithm=algorithm, @@ -439,13 +433,10 @@ def test_core_point_prop3(): @pytest.mark.parametrize("datatype", [np.float32, np.float64]) -@pytest.mark.parametrize("use_handle", [True, False]) @pytest.mark.parametrize("out_dtype", ["int32", np.int32, "int64", np.int64]) @pytest.mark.parametrize("algorithm", ["brute", "rbc"]) @pytest.mark.parametrize("n_samples", [unit_param(500), stress_param(5000)]) -def test_dbscan_propagation( - datatype, use_handle, out_dtype, algorithm, n_samples -): +def test_dbscan_propagation(datatype, out_dtype, algorithm, n_samples): if algorithm == "rbc": if datatype == np.float64 or out_dtype in ["int32", np.int32]: pytest.skip("RBC does not support float64 dtype or int32 labels") @@ -459,10 +450,8 @@ def test_dbscan_propagation( ) X = X.astype(datatype) - handle, stream = get_handle(use_handle) eps = 0.5 cuml_dbscan = cuDBSCAN( - handle=handle, eps=eps, min_samples=5, algorithm=algorithm, diff --git a/python/cuml/tests/test_hdbscan.py b/python/cuml/tests/test_hdbscan.py index ac3bfb8821..a97dfbbe2a 100644 --- a/python/cuml/tests/test_hdbscan.py +++ b/python/cuml/tests/test_hdbscan.py @@ -8,7 +8,6 @@ import pytest import sklearn from packaging.version import Version -from pylibraft.common import DeviceResourcesSNMG from sklearn import datasets from sklearn.datasets import make_blobs from sklearn.model_selection import train_test_split @@ -1196,9 +1195,9 @@ def test_approximate_predict_output_type(): @pytest.mark.parametrize("n_clusters", [1, 4, 7]) @pytest.mark.parametrize("build_algo", ["nn_descent", "brute_force"]) -@pytest.mark.parametrize("do_snmg", [False, True]) +@pytest.mark.parametrize("device_ids", [None, "all"]) @pytest.mark.parametrize("min_samples", [15, 30]) -def test_hdbscan_build_algo(n_clusters, build_algo, do_snmg, min_samples): +def test_hdbscan_build_algo(n_clusters, build_algo, device_ids, min_samples): X, y = make_blobs( n_samples=10_000, n_features=16, @@ -1206,14 +1205,10 @@ def test_hdbscan_build_algo(n_clusters, build_algo, do_snmg, min_samples): random_state=42, ) - hdbscan_handle = None - if do_snmg: - hdbscan_handle = DeviceResourcesSNMG() - cuml_agg = HDBSCAN( build_algo=build_algo, build_kwds={"knn_n_clusters": n_clusters, "nnd_graph_degree": 32}, - handle=hdbscan_handle, + device_ids=device_ids, min_samples=min_samples, ).fit(X) From 669042d5b19a7f87e4bc2d27ce4f70b085567936 Mon Sep 17 00:00:00 2001 From: Jim Crist-Harif Date: Tue, 16 Dec 2025 16:13:22 -0600 Subject: [PATCH 03/21] Use `get_handle` in `cuml.datasets` --- python/cuml/cuml/datasets/arima.pyx | 10 +++------- python/cuml/cuml/datasets/regression.pyx | 10 +++------- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/python/cuml/cuml/datasets/arima.pyx b/python/cuml/cuml/datasets/arima.pyx index 591a5bb691..aeb1279d5d 100644 --- a/python/cuml/cuml/datasets/arima.pyx +++ b/python/cuml/cuml/datasets/arima.pyx @@ -2,15 +2,11 @@ # SPDX-FileCopyrightText: Copyright (c) 2020-2025, NVIDIA CORPORATION. # SPDX-License-Identifier: Apache-2.0 # - -# distutils: language = c++ - from random import randint import numpy as np -from pylibraft.common.handle import Handle -import cuml.internals +from cuml.internals import get_handle, reflect from cuml.internals.array import CumlArray as cumlArray from libc.stdint cimport uint64_t, uintptr_t @@ -54,7 +50,7 @@ inp_to_dtype = { } -@cuml.internals.reflect(array=None) +@reflect(array=None) def make_arima(batch_size=1000, n_obs=100, order=(1, 1, 1), seasonal_order=(0, 0, 0, 0), intercept=False, random_state=None, dtype='double', @@ -112,7 +108,7 @@ def make_arima(batch_size=1000, n_obs=100, order=(1, 1, 1), else: dtype = inp_to_dtype[dtype] - handle = Handle() if handle is None else handle + handle = get_handle(handle=handle) cdef handle_t* handle_ = handle.getHandle() out = cumlArray.empty((n_obs, batch_size), dtype=dtype, order='F') diff --git a/python/cuml/cuml/datasets/regression.pyx b/python/cuml/cuml/datasets/regression.pyx index 4511d9a91f..1fb76a295a 100644 --- a/python/cuml/cuml/datasets/regression.pyx +++ b/python/cuml/cuml/datasets/regression.pyx @@ -2,17 +2,13 @@ # SPDX-FileCopyrightText: Copyright (c) 2019-2025, NVIDIA CORPORATION. # SPDX-License-Identifier: Apache-2.0 # - -# distutils: language = c++ - import typing from random import randint import numpy as np -from pylibraft.common.handle import Handle -import cuml.internals import cuml.internals.nvtx as nvtx +from cuml.internals import get_handle, reflect from cuml.internals.array import CumlArray from libc.stdint cimport uint64_t, uintptr_t @@ -63,7 +59,7 @@ inp_to_dtype = { @nvtx.annotate(message="datasets.make_regression", domain="cuml_python") -@cuml.internals.reflect(array=None) +@reflect(array=None) def make_regression( n_samples=100, n_features=2, @@ -165,7 +161,7 @@ def make_regression( if effective_rank is None: effective_rank = -1 - handle = Handle() if handle is None else handle + handle = get_handle(handle=handle) cdef handle_t* handle_ = handle.getHandle() out = CumlArray.zeros((n_samples, n_features), dtype=dtype, order='C') From 493d9059017d6c1740e4c810380e4ebbce16ca6c Mon Sep 17 00:00:00 2001 From: Jim Crist-Harif Date: Tue, 16 Dec 2025 16:20:01 -0600 Subject: [PATCH 04/21] Use `get_handle` in `cuml.decomposition` --- python/cuml/cuml/decomposition/pca.pyx | 18 +++++++------ python/cuml/cuml/decomposition/pca_mg.pyx | 7 ++--- python/cuml/cuml/decomposition/tsvd.pyx | 17 +++++++----- python/cuml/cuml/decomposition/tsvd_mg.pyx | 7 ++--- python/cuml/tests/test_pca.py | 30 ++++++---------------- python/cuml/tests/test_tsvd.py | 19 ++++---------- 6 files changed, 41 insertions(+), 57 deletions(-) diff --git a/python/cuml/cuml/decomposition/pca.pyx b/python/cuml/cuml/decomposition/pca.pyx index a2a242d031..0005cabde0 100644 --- a/python/cuml/cuml/decomposition/pca.pyx +++ b/python/cuml/cuml/decomposition/pca.pyx @@ -2,7 +2,6 @@ # SPDX-FileCopyrightText: Copyright (c) 2019-2025, NVIDIA CORPORATION. # SPDX-License-Identifier: Apache-2.0 # - import cupy as cp import cupyx.scipy.sparse import numpy as np @@ -14,7 +13,7 @@ from cuml.common.doc_utils import generate_docstring from cuml.common.exceptions import NotFittedError from cuml.common.sparse_utils import is_sparse from cuml.internals.array import CumlArray -from cuml.internals.base import Base +from cuml.internals.base import Base, get_handle from cuml.internals.input_utils import input_to_cuml_array from cuml.internals.interop import ( InteropMixin, @@ -397,7 +396,8 @@ class PCA(Base, cdef uintptr_t mean_ptr = mean.ptr cdef uintptr_t noise_variance_ptr = noise_variance.ptr cdef bool fit_float32 = (X.dtype == np.float32) - cdef handle_t* handle_ = self.handle.getHandle() + handle = get_handle(model=self) + cdef handle_t* handle_ = handle.getHandle() cdef bool flip_signs_based_on_U = self._u_based_sign_flip # Perform fit @@ -428,7 +428,7 @@ class PCA(Base, params, flip_signs_based_on_U ) - self.handle.sync() + handle.sync() # Store results self.components_ = components @@ -565,7 +565,8 @@ class PCA(Base, cdef uintptr_t singular_values_ptr = self.singular_values_.ptr cdef uintptr_t mean_ptr = self.mean_.ptr cdef bool use_float32 = dtype == np.float32 - cdef handle_t* h_ = self.handle.getHandle() + handle = get_handle(model=self) + cdef handle_t* h_ = handle.getHandle() with nogil: if use_float32: @@ -584,7 +585,7 @@ class PCA(Base, mean_ptr, X_inv_ptr, params) - self.handle.sync() + handle.sync() return out @@ -658,7 +659,8 @@ class PCA(Base, cdef uintptr_t singular_values_ptr = self.singular_values_.ptr cdef uintptr_t mean_ptr = self.mean_.ptr cdef bool use_float32 = dtype == np.float32 - cdef handle_t* handle_ = self.handle.getHandle() + handle = get_handle(model=self) + cdef handle_t* handle_ = handle.getHandle() with nogil: if use_float32: @@ -681,7 +683,7 @@ class PCA(Base, mean_ptr, params ) - self.handle.sync() + handle.sync() return out @generate_docstring(X='dense_sparse', diff --git a/python/cuml/cuml/decomposition/pca_mg.pyx b/python/cuml/cuml/decomposition/pca_mg.pyx index cd4ab19048..39a49de49f 100644 --- a/python/cuml/cuml/decomposition/pca_mg.pyx +++ b/python/cuml/cuml/decomposition/pca_mg.pyx @@ -6,7 +6,7 @@ import numpy as np from cuml.decomposition import PCA from cuml.decomposition.base_mg import BaseDecompositionMG -from cuml.internals import run_in_internal_context +from cuml.internals import get_handle, run_in_internal_context from cuml.internals.array import CumlArray from cython.operator cimport dereference as deref @@ -90,7 +90,8 @@ class PCAMG(BaseDecompositionMG, PCA): cdef uintptr_t mean_ptr = mean.ptr cdef uintptr_t noise_variance_ptr = noise_variance.ptr cdef bool use_float32 = (dtype == np.float32) - cdef handle_t* handle_ = self.handle.getHandle() + handle = get_handle(model=self) + cdef handle_t* handle_ = handle.getHandle() cdef bool flip_signs_based_on_U = self._u_based_sign_flip # Perform fit @@ -125,7 +126,7 @@ class PCAMG(BaseDecompositionMG, PCA): False, flip_signs_based_on_U ) - self.handle.sync() + handle.sync() # Store results self.components_ = components diff --git a/python/cuml/cuml/decomposition/tsvd.pyx b/python/cuml/cuml/decomposition/tsvd.pyx index 6496bd0d1a..5fbcfb2520 100644 --- a/python/cuml/cuml/decomposition/tsvd.pyx +++ b/python/cuml/cuml/decomposition/tsvd.pyx @@ -10,7 +10,7 @@ from cuml.common import input_to_cuml_array from cuml.common.array_descriptor import CumlArrayDescriptor from cuml.common.doc_utils import generate_docstring from cuml.internals.array import CumlArray -from cuml.internals.base import Base +from cuml.internals.base import Base, get_handle from cuml.internals.interop import InteropMixin, to_cpu, to_gpu from cuml.internals.mixins import FMajorInputTagMixin @@ -349,7 +349,8 @@ class TruncatedSVD(Base, cdef uintptr_t singular_values_ptr = singular_values.ptr cdef uintptr_t out_ptr = out.ptr cdef bool use_float32 = dtype == np.float32 - cdef handle_t* handle_ = self.handle.getHandle() + handle = get_handle(model=self) + cdef handle_t* handle_ = handle.getHandle() # Perform fit with nogil: @@ -377,7 +378,7 @@ class TruncatedSVD(Base, params, flip_signs_based_on_U ) - self.handle.sync() + handle.sync() # Store results self.components_ = components @@ -419,7 +420,8 @@ class TruncatedSVD(Base, cdef uintptr_t out_ptr = out.ptr cdef uintptr_t components_ptr = self.components_.ptr cdef bool use_float32 = dtype == np.float32 - cdef handle_t* handle_ = self.handle.getHandle() + handle = get_handle(model=self) + cdef handle_t* handle_ = handle.getHandle() with nogil: if use_float32: @@ -438,7 +440,7 @@ class TruncatedSVD(Base, out_ptr, params ) - self.handle.sync() + handle.sync() return out @@ -471,7 +473,8 @@ class TruncatedSVD(Base, cdef uintptr_t out_ptr = out.ptr cdef uintptr_t components_ptr = self.components_.ptr cdef bool use_float32 = dtype == np.float32 - cdef handle_t* handle_ = self.handle.getHandle() + handle = get_handle(model=self) + cdef handle_t* handle_ = handle.getHandle() with nogil: if use_float32: @@ -490,6 +493,6 @@ class TruncatedSVD(Base, out_ptr, params ) - self.handle.sync() + handle.sync() return out diff --git a/python/cuml/cuml/decomposition/tsvd_mg.pyx b/python/cuml/cuml/decomposition/tsvd_mg.pyx index 0db28cb73d..b65fa8df65 100644 --- a/python/cuml/cuml/decomposition/tsvd_mg.pyx +++ b/python/cuml/cuml/decomposition/tsvd_mg.pyx @@ -6,7 +6,7 @@ import numpy as np from cuml.decomposition import TruncatedSVD from cuml.decomposition.base_mg import BaseDecompositionMG -from cuml.internals import run_in_internal_context +from cuml.internals import get_handle, run_in_internal_context from cuml.internals.array import CumlArray from cython.operator cimport dereference as deref @@ -96,7 +96,8 @@ class TSVDMG(BaseDecompositionMG, TruncatedSVD): cdef uintptr_t explained_variance_ratio_ptr = explained_variance_ratio.ptr cdef uintptr_t singular_values_ptr = singular_values.ptr cdef bool use_float32 = dtype == np.float32 - cdef handle_t* handle_ = self.handle.getHandle() + handle = get_handle(model=self) + cdef handle_t* handle_ = handle.getHandle() cdef bool flip_signs_based_on_U = self._u_based_sign_flip # Perform Fit @@ -131,7 +132,7 @@ class TSVDMG(BaseDecompositionMG, TruncatedSVD): False, flip_signs_based_on_U ) - self.handle.sync() + handle.sync() # Store results self.components_ = components diff --git a/python/cuml/tests/test_pca.py b/python/cuml/tests/test_pca.py index e3594b115c..2b84b55757 100644 --- a/python/cuml/tests/test_pca.py +++ b/python/cuml/tests/test_pca.py @@ -17,7 +17,6 @@ from cuml.common.exceptions import NotFittedError from cuml.testing.utils import ( array_equal, - get_handle, quality_param, stress_param, unit_param, @@ -28,9 +27,8 @@ @pytest.mark.parametrize("datatype", [np.float32, np.float64]) @pytest.mark.parametrize("input_type", ["ndarray"]) -@pytest.mark.parametrize("use_handle", [True, False]) @pytest.mark.parametrize("sparse", [True, False]) -def test_pca_fit(datatype, input_type, sparse, use_handle): +def test_pca_fit(datatype, input_type, sparse): solver = "auto" if sparse: X = scipy.sparse.random( @@ -58,10 +56,8 @@ def test_pca_fit(datatype, input_type, sparse, use_handle): skpca = skPCA(n_components=2, svd_solver=solver) skpca.fit(X) - handle, stream = get_handle(use_handle) - cupca = cuPCA(n_components=2, handle=handle) + cupca = cuPCA(n_components=2) cupca.fit(X) - cupca.handle.sync() for attr in [ "singular_values_", @@ -109,7 +105,6 @@ def test_pca_defaults(n_samples, n_features, sparse): cupca = cuPCA() cupca.fit(X) curesult = cupca.transform(X) - cupca.handle.sync() if sparse: X = X.toarray().get() @@ -125,11 +120,10 @@ def test_pca_defaults(n_samples, n_features, sparse): @pytest.mark.parametrize("datatype", [np.float32, np.float64]) @pytest.mark.parametrize("input_type", ["ndarray"]) -@pytest.mark.parametrize("use_handle", [True, False]) @pytest.mark.parametrize( "name", [unit_param(None), quality_param("iris"), stress_param("blobs")] ) -def test_pca_fit_then_transform(datatype, input_type, name, use_handle): +def test_pca_fit_then_transform(datatype, input_type, name): # Assume at least 4GB memory max_gpu_memory = pytest.max_gpu_memory or 4 @@ -166,12 +160,10 @@ def test_pca_fit_then_transform(datatype, input_type, name, use_handle): skpca.fit(X) Xskpca = skpca.transform(X) - handle, stream = get_handle(use_handle) - cupca = cuPCA(n_components=2, handle=handle) + cupca = cuPCA(n_components=2) cupca.fit(X) X_cupca = cupca.transform(X) - cupca.handle.sync() if name != "blobs": assert array_equal(X_cupca, Xskpca, 1e-3, with_sign=True) @@ -181,11 +173,10 @@ def test_pca_fit_then_transform(datatype, input_type, name, use_handle): @pytest.mark.parametrize("datatype", [np.float32, np.float64]) @pytest.mark.parametrize("input_type", ["ndarray"]) -@pytest.mark.parametrize("use_handle", [True, False]) @pytest.mark.parametrize( "name", [unit_param(None), quality_param("iris"), stress_param("blobs")] ) -def test_pca_fit_transform(datatype, input_type, name, use_handle): +def test_pca_fit_transform(datatype, input_type, name): # Assume at least 4GB memory max_gpu_memory = pytest.max_gpu_memory or 4 @@ -222,11 +213,9 @@ def test_pca_fit_transform(datatype, input_type, name, use_handle): skpca = skPCA(n_components=2) Xskpca = skpca.fit_transform(X) - handle, stream = get_handle(use_handle) - cupca = cuPCA(n_components=2, handle=handle) + cupca = cuPCA(n_components=2) X_cupca = cupca.fit_transform(X) - cupca.handle.sync() if name != "blobs": assert array_equal(X_cupca, Xskpca, 1e-3, with_sign=True) @@ -236,12 +225,11 @@ def test_pca_fit_transform(datatype, input_type, name, use_handle): @pytest.mark.parametrize("datatype", [np.float32, np.float64]) @pytest.mark.parametrize("input_type", ["ndarray"]) -@pytest.mark.parametrize("use_handle", [True, False]) @pytest.mark.parametrize( "name", [unit_param(None), quality_param("quality"), stress_param("blobs")] ) @pytest.mark.parametrize("nrows", [unit_param(500), quality_param(5000)]) -def test_pca_inverse_transform(datatype, input_type, name, use_handle, nrows): +def test_pca_inverse_transform(datatype, input_type, name, nrows): if name == "blobs": pytest.skip("fails when using blobs dataset") X, y = make_blobs(n_samples=500000, n_features=1000, random_state=0) @@ -253,13 +241,11 @@ def test_pca_inverse_transform(datatype, input_type, name, use_handle, nrows): X[:, 1] *= 0.00001 # make middle component relatively small X += [3, 4, 2] # make a large mean - handle, stream = get_handle(use_handle) - cupca = cuPCA(n_components=2, handle=handle) + cupca = cuPCA(n_components=2) X_cupca = cupca.fit_transform(X) input_gdf = cupca.inverse_transform(X_cupca) - cupca.handle.sync() assert array_equal(input_gdf, X, 5e-5, with_sign=True) diff --git a/python/cuml/tests/test_tsvd.py b/python/cuml/tests/test_tsvd.py index 89fead42a2..d1f456fdef 100644 --- a/python/cuml/tests/test_tsvd.py +++ b/python/cuml/tests/test_tsvd.py @@ -10,7 +10,6 @@ from cuml import TruncatedSVD as cuTSVD from cuml.testing.utils import ( array_equal, - get_handle, quality_param, stress_param, unit_param, @@ -18,11 +17,10 @@ @pytest.mark.parametrize("datatype", [np.float32, np.float64]) -@pytest.mark.parametrize("use_handle", [True, False]) @pytest.mark.parametrize( "name", [unit_param(None), quality_param("random"), stress_param("blobs")] ) -def test_tsvd_fit(datatype, name, use_handle): +def test_tsvd_fit(datatype, name): if name == "blobs": X, y = make_blobs(n_samples=500000, n_features=1000, random_state=0) @@ -43,11 +41,9 @@ def test_tsvd_fit(datatype, name, use_handle): sktsvd = skTSVD(n_components=1) sktsvd.fit(X) - handle, stream = get_handle(use_handle) - cutsvd = cuTSVD(n_components=1, handle=handle) + cutsvd = cuTSVD(n_components=1) cutsvd.fit(X) - cutsvd.handle.sync() if name != "blobs": for attr in [ @@ -64,11 +60,10 @@ def test_tsvd_fit(datatype, name, use_handle): @pytest.mark.parametrize("datatype", [np.float32, np.float64]) -@pytest.mark.parametrize("use_handle", [True, False]) @pytest.mark.parametrize( "name", [unit_param(None), quality_param("random"), stress_param("blobs")] ) -def test_tsvd_fit_transform(datatype, name, use_handle): +def test_tsvd_fit_transform(datatype, name): if name == "blobs": X, y = make_blobs(n_samples=500000, n_features=1000, random_state=0) @@ -89,22 +84,19 @@ def test_tsvd_fit_transform(datatype, name, use_handle): skpca = skTSVD(n_components=1) Xsktsvd = skpca.fit_transform(X) - handle, stream = get_handle(use_handle) - cutsvd = cuTSVD(n_components=1, handle=handle) + cutsvd = cuTSVD(n_components=1) Xcutsvd = cutsvd.fit_transform(X) - cutsvd.handle.sync() if name != "blobs": assert array_equal(Xcutsvd, Xsktsvd, 1e-3, with_sign=True) @pytest.mark.parametrize("datatype", [np.float32, np.float64]) -@pytest.mark.parametrize("use_handle", [True, False]) @pytest.mark.parametrize( "name", [unit_param(None), quality_param("random"), stress_param("blobs")] ) -def test_tsvd_inverse_transform(datatype, name, use_handle): +def test_tsvd_inverse_transform(datatype, name): if name == "blobs": pytest.skip("fails when using blobs dataset") X, y = make_blobs(n_samples=500000, n_features=1000, random_state=0) @@ -126,5 +118,4 @@ def test_tsvd_inverse_transform(datatype, name, use_handle): Xcutsvd = cutsvd.fit_transform(X) input_gdf = cutsvd.inverse_transform(Xcutsvd) - cutsvd.handle.sync() assert array_equal(input_gdf, X, 0.4, with_sign=True) From 156b0e6810ecc4ddf9829f7b1833f0c53b366cef Mon Sep 17 00:00:00 2001 From: Jim Crist-Harif Date: Wed, 17 Dec 2025 15:41:46 -0600 Subject: [PATCH 05/21] Use `get_handle` in `cuml.ensemble`/`cuml.fil` Also drops some xfailed (and long since broken) memleak tests. Better to delete than edit in a way that still doesn't fix them. --- .../cuml/ensemble/randomforest_common.pyx | 9 +- python/cuml/cuml/fil/fil.pyx | 4 +- python/cuml/tests/test_random_forest.py | 213 +----------------- 3 files changed, 8 insertions(+), 218 deletions(-) diff --git a/python/cuml/cuml/ensemble/randomforest_common.pyx b/python/cuml/cuml/ensemble/randomforest_common.pyx index 7535c89d17..5268eaf165 100644 --- a/python/cuml/cuml/ensemble/randomforest_common.pyx +++ b/python/cuml/cuml/ensemble/randomforest_common.pyx @@ -11,10 +11,9 @@ from typing import Literal import cupy as cp import numpy as np import treelite.sklearn -from pylibraft.common.handle import Handle from cuml.fil.fil import ForestInference -from cuml.internals.base import Base +from cuml.internals.base import Base, get_handle from cuml.internals.interop import ( InteropMixin, UnsupportedOnCPU, @@ -329,9 +328,6 @@ class BaseRandomForestModel(Base, InteropMixin): verbose=False, output_type=None, ): - if handle is None: - handle = Handle(n_streams=n_streams) - super().__init__(handle=handle, verbose=verbose, output_type=output_type) self.split_criterion = split_criterion @@ -478,7 +474,8 @@ class BaseRandomForestModel(Base, InteropMixin): ) cdef TreeliteModelHandle tl_handle - cdef handle_t* handle_ = self.handle.getHandle() + handle = get_handle(model=self, n_streams=self.n_streams) + cdef handle_t* handle_ = handle.getHandle() # Store oob_score in C variable for nogil block cdef bool use_oob_score = self.oob_score diff --git a/python/cuml/cuml/fil/fil.pyx b/python/cuml/cuml/fil/fil.pyx index d3040329f0..4d8e61aa4f 100644 --- a/python/cuml/cuml/fil/fil.pyx +++ b/python/cuml/cuml/fil/fil.pyx @@ -11,7 +11,7 @@ import treelite.sklearn import cuml.internals.nvtx as nvtx from cuml.internals.array import CumlArray -from cuml.internals.base import Base +from cuml.internals.base import Base, get_handle from cuml.internals.device_type import DeviceType, DeviceTypeError from cuml.internals.global_settings import GlobalSettings from cuml.internals.input_utils import input_to_cuml_array @@ -656,7 +656,7 @@ class ForestInference(Base, CMajorInputTagMixin): else: raise ValueError("treelite_model should be either treelite.Model or bytes") impl = ForestInference_impl( - self.handle, + get_handle(model=self), treelite_model_bytes, layout=self.layout, align_bytes=self.align_bytes, diff --git a/python/cuml/tests/test_random_forest.py b/python/cuml/tests/test_random_forest.py index 1d566886ed..5d690f7678 100644 --- a/python/cuml/tests/test_random_forest.py +++ b/python/cuml/tests/test_random_forest.py @@ -13,7 +13,6 @@ import pytest import treelite from cudf.pandas import LOADED as cudf_pandas_active -from numba import cuda from sklearn.datasets import ( fetch_california_housing, load_breast_cancer, @@ -31,17 +30,11 @@ from sklearn.model_selection import train_test_split import cuml -import cuml.internals.logger as logger from cuml.ensemble import RandomForestClassifier as curfc from cuml.ensemble import RandomForestRegressor as curfr from cuml.ensemble.randomforest_common import compute_max_features from cuml.metrics import r2_score -from cuml.testing.utils import ( - get_handle, - quality_param, - stress_param, - unit_param, -) +from cuml.testing.utils import quality_param, stress_param, unit_param @pytest.fixture( @@ -203,11 +196,7 @@ def test_default_parameters(): assert clf_params["split_criterion"] == "gini" # Drop differing params - for name in [ - "max_features", - "split_criterion", - "handle", - ]: + for name in ["max_features", "split_criterion"]: reg_params.pop(name) clf_params.pop(name) @@ -290,17 +279,12 @@ def test_tweedie_convergence(max_depth, split_criterion): "Issue: https://github.com/rapidsai/cuml/issues/5991", ) def test_rf_classification(small_clf, datatype, max_samples, max_features): - use_handle = True - X, y = small_clf X = X.astype(datatype) y = y.astype(np.int32) X_train, X_test, y_train, y_test = train_test_split( X, y, train_size=0.8, random_state=0 ) - # Create a handle for the cuml model - handle, stream = get_handle(use_handle, n_streams=1) - # Initialize, fit and predict using cuML's # random forest classification model cuml_model = curfc( @@ -312,7 +296,6 @@ def test_rf_classification(small_clf, datatype, max_samples, max_features): random_state=123, n_streams=1, n_estimators=40, - handle=handle, max_leaves=-1, max_depth=16, ) @@ -343,8 +326,6 @@ def test_rf_classification(small_clf, datatype, max_samples, max_features): def test_rf_classification_unorder( small_clf, datatype, max_samples, max_features=1, a=2, b=5 ): - use_handle = True - X, y = small_clf X = X.astype(datatype) y = y.astype(np.int32) @@ -353,8 +334,6 @@ def test_rf_classification_unorder( X_train, X_test, y_train, y_test = train_test_split( X, y, train_size=0.8, random_state=0 ) - # Create a handle for the cuml model - handle, stream = get_handle(use_handle, n_streams=1) # Initialize, fit and predict using cuML's # random forest classification model @@ -367,7 +346,6 @@ def test_rf_classification_unorder( random_state=123, n_streams=1, n_estimators=40, - handle=handle, max_leaves=-1, max_depth=16, ) @@ -415,8 +393,6 @@ def test_rf_classification_unorder( def test_rf_regression( special_reg, datatype, max_features, max_samples, n_bins ): - use_handle = True - X, y = special_reg X = X.astype(datatype) y = y.astype(datatype) @@ -424,9 +400,6 @@ def test_rf_regression( X, y, train_size=0.8, random_state=0 ) - # Create a handle for the cuml model - handle, stream = get_handle(use_handle, n_streams=1) - # Initialize and fit using cuML's random forest regression model cuml_model = curfr( max_features=max_features, @@ -437,7 +410,6 @@ def test_rf_regression( random_state=123, n_streams=1, n_estimators=50, - handle=handle, max_leaves=-1, max_depth=16, ) @@ -581,8 +553,6 @@ def rf_classification( ) X_test = X_test.astype(datatype[1]) - n_streams = 1 - handle, stream = get_handle(True, n_streams=n_streams) # Initialize, fit and predict using cuML's # random forest classification model cuml_model = curfc( @@ -593,10 +563,9 @@ def rf_classification( min_samples_leaf=2, random_state=999, n_estimators=40, - handle=handle, max_leaves=-1, max_depth=16, - n_streams=n_streams, + n_streams=1, ) if array_type == "dataframe": X_train_df = cudf.DataFrame(X_train) @@ -668,7 +637,6 @@ def test_rf_classification_proba( "Issue: https://github.com/rapidsai/cuml/issues/5991", ) def test_rf_classification_sparse(small_clf, datatype, fil_layout): - use_handle = True num_trees = 50 X, y = small_clf @@ -678,9 +646,6 @@ def test_rf_classification_sparse(small_clf, datatype, fil_layout): X, y, train_size=0.8, random_state=0 ) - # Create a handle for the cuml model - handle, stream = get_handle(use_handle, n_streams=1) - # Initialize, fit and predict using cuML's # random forest classification model cuml_model = curfc( @@ -690,7 +655,6 @@ def test_rf_classification_sparse(small_clf, datatype, fil_layout): random_state=123, n_streams=1, n_estimators=num_trees, - handle=handle, max_leaves=-1, max_depth=40, ) @@ -735,7 +699,6 @@ def test_rf_classification_sparse(small_clf, datatype, fil_layout): "Issue: https://github.com/rapidsai/cuml/issues/5991", ) def test_rf_regression_sparse(special_reg, datatype, fil_layout): - use_handle = True num_trees = 50 X, y = special_reg @@ -745,9 +708,6 @@ def test_rf_regression_sparse(special_reg, datatype, fil_layout): X, y, train_size=0.8, random_state=0 ) - # Create a handle for the cuml model - handle, stream = get_handle(use_handle, n_streams=1) - # Initialize and fit using cuML's random forest regression model cuml_model = curfr( n_bins=16, @@ -756,7 +716,6 @@ def test_rf_regression_sparse(special_reg, datatype, fil_layout): random_state=123, n_streams=1, n_estimators=num_trees, - handle=handle, max_leaves=-1, max_depth=40, ) @@ -793,56 +752,6 @@ def test_rf_regression_sparse(special_reg, datatype, fil_layout): assert r2 >= (sk_r2 - 0.08) -@pytest.mark.xfail(reason="Need rapidsai/rmm#415 to detect memleak robustly") -@pytest.mark.memleak -@pytest.mark.parametrize("datatype", [np.float32, np.float64]) -@pytest.mark.parametrize( - "fil_layout", ["depth_first", "breadth_first", "layered"] -) -@pytest.mark.parametrize( - "n_iter", [unit_param(5), quality_param(30), stress_param(80)] -) -def test_rf_memory_leakage(small_clf, datatype, fil_layout, n_iter): - use_handle = True - - X, y = small_clf - X = X.astype(datatype) - y = y.astype(np.int32) - X_train, X_test, y_train, y_test = train_test_split( - X, y, train_size=0.8, random_state=0 - ) - - # Create a handle for the cuml model - handle, stream = get_handle(use_handle, n_streams=1) - - # Warmup. Some modules that are used in RF allocate space on the device - # and consume memory. This is to make sure that the allocation is done - # before the first call to get_memory_info. - base_model = curfc(handle=handle) - base_model.fit(X_train, y_train) - handle.sync() # just to be sure - free_mem = cuda.current_context().get_memory_info()[0] - - def test_for_memory_leak(): - nonlocal free_mem - cuml_mods = curfc(handle=handle) - cuml_mods.fit(X_train, y_train) - handle.sync() # just to be sure - # Calculate the memory free after fitting the cuML model - delta_mem = free_mem - cuda.current_context().get_memory_info()[0] - assert delta_mem == 0 - - for i in range(2): - cuml_mods.predict(X_test, layout=fil_layout) - handle.sync() # just to be sure - # Calculate the memory free after predicting the cuML model - delta_mem = free_mem - cuda.current_context().get_memory_info()[0] - assert delta_mem == 0 - - for i in range(n_iter): - test_for_memory_leak() - - @pytest.mark.parametrize("max_features", [1.0, "log2", "sqrt"]) @pytest.mark.parametrize("max_depth", [10, 13, 16]) @pytest.mark.parametrize("n_estimators", [10, 20, 100]) @@ -926,122 +835,6 @@ def test_multiple_fits_regression(column_info, nrows, n_estimators, n_bins): assert params["n_bins"] == n_bins -@pytest.mark.xfail( - reason="Needs refactoring/debugging due to sporadic failures" - "https://github.com/rapidsai/cuml/issues/5528" -) -@pytest.mark.memleak -@pytest.mark.parametrize("estimator_type", ["classification"]) -def test_rf_host_memory_leak(large_clf, estimator_type): - import gc - import os - - try: - import psutil - except ImportError: - pytest.skip("psutil not installed") - - process = psutil.Process(os.getpid()) - - X, y = large_clf - X = X.astype(np.float32) - params = {"max_depth": 50} - if estimator_type == "classification": - base_model = curfc(max_depth=10, n_estimators=100, random_state=123) - y = y.astype(np.int32) - else: - base_model = curfr(max_depth=10, n_estimators=100, random_state=123) - y = y.astype(np.float32) - - # Pre-fit once - this is our baseline and memory usage - # should not significantly exceed it after later fits - base_model.fit(X, y) - gc.collect() - initial_baseline_mem = process.memory_info().rss - - for i in range(5): - base_model.fit(X, y) - base_model.set_params(**params) - gc.collect() - final_mem = process.memory_info().rss - - # Some tiny allocations may occur, but we should not leak - # without bounds, which previously happened - assert (final_mem - initial_baseline_mem) < 2.4e6 - - -@pytest.mark.xfail( - reason="Needs refactoring/debugging due to sporadic failures" - "https://github.com/rapidsai/cuml/issues/5528" -) -@pytest.mark.memleak -@pytest.mark.parametrize("estimator_type", ["regression", "classification"]) -@pytest.mark.parametrize("i", list(range(100))) -def test_concat_memory_leak(large_clf, estimator_type, i): - import gc - import os - - try: - import psutil - except ImportError: - pytest.skip("psutil not installed") - - process = psutil.Process(os.getpid()) - - X, y = large_clf - X = X.astype(np.float32) - - # Build a series of RF models - n_models = 10 - if estimator_type == "classification": - base_models = [ - curfc(max_depth=10, n_estimators=100, random_state=123) - for i in range(n_models) - ] - y = y.astype(np.int32) - elif estimator_type == "regression": - base_models = [ - curfr(max_depth=10, n_estimators=100, random_state=123) - for i in range(n_models) - ] - y = y.astype(np.float32) - else: - assert False - - # Pre-fit once - this is our baseline and memory usage - # should not significantly exceed it after later fits - for model in base_models: - model.fit(X, y) - - # Just concatenate over and over in a loop - concat_models = base_models[1:] - init_model = base_models[0] - other_handles = [ - model._obtain_treelite_handle() for model in concat_models - ] - init_model._concatenate_treelite_handle(other_handles) - - gc.collect() - initial_baseline_mem = process.memory_info().rss - for i in range(10): - init_model._concatenate_treelite_handle(other_handles) - gc.collect() - used_mem = process.memory_info().rss - logger.debug( - "memory at rep %2d: %d m" - % (i, (used_mem - initial_baseline_mem) / 1e6) - ) - - gc.collect() - used_mem = process.memory_info().rss - logger.info( - "Final memory delta: %d" % ((used_mem - initial_baseline_mem) / 1e6) - ) - - # increasing margin to avoid very infrequent failures - assert (used_mem - initial_baseline_mem) < 1.1e6 - - def test_rf_nbins_small(small_clf): X, y = small_clf X = X.astype(np.float32) From 534a32801908026431380060dd971343b4ee35fb Mon Sep 17 00:00:00 2001 From: Jim Crist-Harif Date: Wed, 17 Dec 2025 15:46:29 -0600 Subject: [PATCH 06/21] Use `get_handle` in `cuml.experimental` --- python/cuml/cuml/experimental/linear_model/lars.pyx | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/python/cuml/cuml/experimental/linear_model/lars.pyx b/python/cuml/cuml/experimental/linear_model/lars.pyx index 9761ad5cf4..e1037712bb 100644 --- a/python/cuml/cuml/experimental/linear_model/lars.pyx +++ b/python/cuml/cuml/experimental/linear_model/lars.pyx @@ -11,7 +11,7 @@ from cuml.common.array_descriptor import CumlArrayDescriptor from cuml.common.doc_utils import generate_docstring from cuml.internals import logger, reflect from cuml.internals.array import CumlArray -from cuml.internals.base import Base +from cuml.internals.base import Base, get_handle from cuml.internals.mixins import RegressorMixin from libc.stdint cimport uintptr_t @@ -201,7 +201,8 @@ class Lars(Base, RegressorMixin): def _fit_cpp(self, X, y, Gram, x_scale, convert_dtype): """ Fit lars model using cpp solver""" - cdef handle_t* handle_ = self.handle.getHandle() + handle = get_handle(model=self) + cdef handle_t* handle_ = handle.getHandle() X_m, _, _, _ = \ input_to_cuml_array(X, convert_to_dtype=(np.float32 if convert_dtype @@ -252,6 +253,7 @@ class Lars(Base, RegressorMixin): alphas_ptr, &n_active, Gram_ptr, max_iter, coef_path_ptr, self._verbose_level, ld_X, ld_G, self.eps) + handle.sync() self.n_active = n_active self.n_iter_ = n_active @@ -310,8 +312,6 @@ class Lars(Base, RegressorMixin): self._set_intercept(x_mean, x_scale, y_scale) - self.handle.sync() - del X_m del y_m del Gram @@ -346,7 +346,8 @@ class Lars(Base, RegressorMixin): X, check_dtype=self.dtype, convert_to_dtype=conv_dtype, check_cols=self.n_cols, order='F') - cdef handle_t* handle_ = self.handle.getHandle() + handle = get_handle(model=self) + cdef handle_t* handle_ = handle.getHandle() cdef uintptr_t X_ptr = X_m.ptr cdef int ld_X = n_rows cdef uintptr_t beta_ptr = input_to_cuml_array(self.beta_).array.ptr @@ -368,7 +369,7 @@ class Lars(Base, RegressorMixin): self.intercept_, preds.ptr) - self.handle.sync() + handle.sync() del X_m return preds From da9b6c2528fdf9b5585caae3c78081c01eed0bf4 Mon Sep 17 00:00:00 2001 From: Jim Crist-Harif Date: Wed, 17 Dec 2025 16:03:57 -0600 Subject: [PATCH 07/21] Use `get_handle` in `cuml.linear_model`/`cuml.solvers` --- python/cuml/cuml/linear_model/elastic_net.py | 6 ++--- .../cuml/linear_model/linear_regression.pyx | 14 ++++------- .../linear_model/linear_regression_mg.pyx | 7 +++--- .../cuml/linear_model/logistic_regression.py | 4 +-- .../linear_model/logistic_regression_mg.pyx | 7 +++--- .../cuml/linear_model/mbsgd_classifier.py | 4 +-- .../cuml/cuml/linear_model/mbsgd_regressor.py | 4 +-- python/cuml/cuml/linear_model/ridge.pyx | 7 +++--- python/cuml/cuml/linear_model/ridge_mg.pyx | 7 +++--- python/cuml/cuml/solvers/cd.pyx | 12 ++++++--- python/cuml/cuml/solvers/cd_mg.pyx | 7 +++--- python/cuml/cuml/solvers/qn.pyx | 7 +++--- python/cuml/cuml/solvers/sgd.pyx | 25 +++++-------------- 13 files changed, 51 insertions(+), 60 deletions(-) diff --git a/python/cuml/cuml/linear_model/elastic_net.py b/python/cuml/cuml/linear_model/elastic_net.py index fb6a9cd820..1fe2230509 100644 --- a/python/cuml/cuml/linear_model/elastic_net.py +++ b/python/cuml/cuml/linear_model/elastic_net.py @@ -5,7 +5,7 @@ from cuml.common.array_descriptor import CumlArrayDescriptor from cuml.common.doc_utils import generate_docstring from cuml.internals.array import CumlArray -from cuml.internals.base import Base +from cuml.internals.base import Base, get_handle from cuml.internals.interop import ( InteropMixin, UnsupportedOnGPU, @@ -249,7 +249,7 @@ def fit( tol=self.tol, penalty_normalized=False, verbose=self._verbose_level, - handle=self.handle, + handle=get_handle(model=self), ) coef = CumlArray(data=coef.to_output("cupy").flatten()) intercept = intercept.item() @@ -265,7 +265,7 @@ def fit( shuffle=self.selection == "random", max_iter=self.max_iter, tol=self.tol, - handle=self.handle, + handle=get_handle(model=self), ) else: raise ValueError(f"solver {self.solver} is not supported") diff --git a/python/cuml/cuml/linear_model/linear_regression.pyx b/python/cuml/cuml/linear_model/linear_regression.pyx index 6ec166f660..17b1b4543c 100644 --- a/python/cuml/cuml/linear_model/linear_regression.pyx +++ b/python/cuml/cuml/linear_model/linear_regression.pyx @@ -7,13 +7,12 @@ import warnings import cupy as cp import numpy as np -from pylibraft.common.handle import Handle from cuml.common import input_to_cuml_array from cuml.common.array_descriptor import CumlArrayDescriptor from cuml.common.doc_utils import generate_docstring from cuml.internals.array import CumlArray, cuda_ptr -from cuml.internals.base import Base +from cuml.internals.base import Base, get_handle from cuml.internals.interop import ( InteropMixin, UnsupportedOnGPU, @@ -269,11 +268,6 @@ class LinearRegression(Base, verbose=False, output_type=None ): - if handle is None and algorithm in ("auto", "eig"): - # if possible, create two streams, so that eigenvalue decomposition - # can benefit from running independent operations concurrently. - handle = Handle(n_streams=2) - super().__init__(handle=handle, verbose=verbose, output_type=output_type) self.algorithm = algorithm @@ -376,7 +370,9 @@ class LinearRegression(Base, cdef bool is_float32 = X_m.dtype == np.float32 cdef float intercept_f32 cdef double intercept_f64 - cdef handle_t* handle_ = self.handle.getHandle() + # Always use 2 streams to expose concurrency in the eig computation + handle = get_handle(model=self, n_streams=2) + cdef handle_t* handle_ = handle.getHandle() cdef bool fit_intercept = self.fit_intercept with nogil: @@ -406,7 +402,7 @@ class LinearRegression(Base, algo, sample_weight_ptr, ) - self.handle.sync() + handle.sync() self.intercept_ = intercept_f32 if is_float32 else intercept_f64 self.coef_ = coef diff --git a/python/cuml/cuml/linear_model/linear_regression_mg.pyx b/python/cuml/cuml/linear_model/linear_regression_mg.pyx index 6d6c6524cb..6ce49c2712 100644 --- a/python/cuml/cuml/linear_model/linear_regression_mg.pyx +++ b/python/cuml/cuml/linear_model/linear_regression_mg.pyx @@ -3,7 +3,7 @@ # SPDX-License-Identifier: Apache-2.0 import numpy as np -from cuml.internals import run_in_internal_context +from cuml.internals import get_handle, run_in_internal_context from cuml.linear_model.base_mg import MGFitMixin from cuml.linear_model.linear_regression import Algo, LinearRegression @@ -46,7 +46,8 @@ class LinearRegressionMG(MGFitMixin, LinearRegression): ) cdef float float_intercept cdef double double_intercept - cdef handle_t* handle_ = self.handle.getHandle() + handle = get_handle(model=self) + cdef handle_t* handle_ = handle.getHandle() if self.dtype == np.float32: @@ -75,4 +76,4 @@ class LinearRegressionMG(MGFitMixin, LinearRegression): self.intercept_ = double_intercept - self.handle.sync() + handle.sync() diff --git a/python/cuml/cuml/linear_model/logistic_regression.py b/python/cuml/cuml/linear_model/logistic_regression.py index 49417e35d9..04d01e0bc4 100644 --- a/python/cuml/cuml/linear_model/logistic_regression.py +++ b/python/cuml/cuml/linear_model/logistic_regression.py @@ -16,7 +16,7 @@ ) from cuml.common.doc_utils import generate_docstring from cuml.internals.array import CumlArray -from cuml.internals.base import Base +from cuml.internals.base import Base, get_handle from cuml.internals.interop import ( InteropMixin, UnsupportedOnGPU, @@ -341,7 +341,7 @@ def fit( tol=self.tol, linesearch_max_iter=self.linesearch_max_iter, verbose=self._verbose_level, - handle=self.handle, + handle=get_handle(model=self), lbfgs_memory=self.lbfgs_memory, penalty_normalized=self.penalty_normalized, ) diff --git a/python/cuml/cuml/linear_model/logistic_regression_mg.pyx b/python/cuml/cuml/linear_model/logistic_regression_mg.pyx index 5e34f63e53..d09e89c8c1 100644 --- a/python/cuml/cuml/linear_model/logistic_regression_mg.pyx +++ b/python/cuml/cuml/linear_model/logistic_regression_mg.pyx @@ -4,7 +4,7 @@ # import numpy as np -from cuml.internals import run_in_internal_context +from cuml.internals import get_handle, run_in_internal_context from cuml.internals.array import CumlArray from cuml.linear_model import LogisticRegression from cuml.linear_model.base_mg import MGFitMixin @@ -146,7 +146,8 @@ class LogisticRegressionMG(MGFitMixin, LogisticRegression): @run_in_internal_context def _fit(self, X, uintptr_t y, uintptr_t coef_ptr, uintptr_t input_desc): - cdef handle_t* handle_ = self.handle.getHandle() + handle = get_handle(model=self) + cdef handle_t* handle_ = handle.getHandle() # Determine the number of classes in y if self.dtype == np.float32: @@ -311,7 +312,7 @@ class LogisticRegressionMG(MGFitMixin, LogisticRegression): &n_iter, ) - self.handle.sync() + handle.sync() # Postprocess coef into coef_ and intercept_ coef_cp = coef.to_output("cupy") diff --git a/python/cuml/cuml/linear_model/mbsgd_classifier.py b/python/cuml/cuml/linear_model/mbsgd_classifier.py index 40af5cc6ab..f1a1fa0bc3 100644 --- a/python/cuml/cuml/linear_model/mbsgd_classifier.py +++ b/python/cuml/cuml/linear_model/mbsgd_classifier.py @@ -8,7 +8,7 @@ from cuml.common.array_descriptor import CumlArrayDescriptor from cuml.common.classification import decode_labels, preprocess_labels from cuml.common.doc_utils import generate_docstring -from cuml.internals.base import Base +from cuml.internals.base import Base, get_handle from cuml.internals.mixins import ClassifierMixin, FMajorInputTagMixin from cuml.linear_model.base import LinearClassifierMixin from cuml.solvers.sgd import fit_sgd @@ -214,7 +214,7 @@ def fit(self, X, y, *, convert_dtype=True) -> "MBSGDClassifier": power_t=self.power_t, batch_size=self.batch_size, n_iter_no_change=self.n_iter_no_change, - handle=self.handle, + handle=get_handle(model=self), ) self.coef_ = coef self.intercept_ = intercept diff --git a/python/cuml/cuml/linear_model/mbsgd_regressor.py b/python/cuml/cuml/linear_model/mbsgd_regressor.py index 8ad22fa95a..73e6994a5e 100644 --- a/python/cuml/cuml/linear_model/mbsgd_regressor.py +++ b/python/cuml/cuml/linear_model/mbsgd_regressor.py @@ -2,7 +2,7 @@ # SPDX-License-Identifier: Apache-2.0 from cuml.common.array_descriptor import CumlArrayDescriptor from cuml.common.doc_utils import generate_docstring -from cuml.internals.base import Base +from cuml.internals.base import Base, get_handle from cuml.internals.mixins import FMajorInputTagMixin, RegressorMixin from cuml.internals.outputs import reflect from cuml.linear_model.base import LinearPredictMixin @@ -196,7 +196,7 @@ def fit(self, X, y, *, convert_dtype=True) -> "MBSGDRegressor": power_t=self.power_t, batch_size=self.batch_size, n_iter_no_change=self.n_iter_no_change, - handle=self.handle, + handle=get_handle(model=self), ) self.coef_ = coef self.intercept_ = intercept diff --git a/python/cuml/cuml/linear_model/ridge.pyx b/python/cuml/cuml/linear_model/ridge.pyx index ee72f33ea7..917ca2f530 100644 --- a/python/cuml/cuml/linear_model/ridge.pyx +++ b/python/cuml/cuml/linear_model/ridge.pyx @@ -8,7 +8,7 @@ import numpy as np from cuml.common.array_descriptor import CumlArrayDescriptor from cuml.common.doc_utils import generate_docstring from cuml.internals.array import CumlArray, cuda_ptr -from cuml.internals.base import Base +from cuml.internals.base import Base, get_handle from cuml.internals.input_utils import input_to_cuml_array from cuml.internals.interop import ( InteropMixin, @@ -351,7 +351,8 @@ class Ridge(Base, cdef float alpha_f32 = alpha cdef double alpha_f64 = alpha - cdef handle_t* handle_ = self.handle.getHandle() + handle = get_handle(model=self) + cdef handle_t* handle_ = handle.getHandle() cdef uintptr_t X_ptr = X_m.ptr cdef uintptr_t y_ptr = y_m.ptr cdef uintptr_t coef_ptr = coef.ptr @@ -392,7 +393,7 @@ class Ridge(Base, 1, sample_weight_ptr, ) - self.handle.sync() + handle.sync() if self.fit_intercept: intercept = intercept_f32 if use_float32 else intercept_f64 diff --git a/python/cuml/cuml/linear_model/ridge_mg.pyx b/python/cuml/cuml/linear_model/ridge_mg.pyx index 4de6b9cad0..32345a17ed 100644 --- a/python/cuml/cuml/linear_model/ridge_mg.pyx +++ b/python/cuml/cuml/linear_model/ridge_mg.pyx @@ -3,7 +3,7 @@ # SPDX-License-Identifier: Apache-2.0 import numpy as np -from cuml.internals import run_in_internal_context +from cuml.internals import get_handle, run_in_internal_context from cuml.linear_model import Ridge from cuml.linear_model.base_mg import MGFitMixin @@ -71,7 +71,8 @@ class RidgeMG(MGFitMixin, Ridge): cdef double intercept_f64 cdef float alpha_f32 = self.alpha cdef double alpha_f64 = self.alpha - cdef handle_t* handle_ = self.handle.getHandle() + handle = get_handle(model=self) + cdef handle_t* handle_ = handle.getHandle() cdef bool use_float32 = self.dtype == np.float32 if use_float32: @@ -99,7 +100,7 @@ class RidgeMG(MGFitMixin, Ridge): algo, False) - self.handle.sync() + handle.sync() self.solver_ = solver self.intercept_ = (intercept_f32 if use_float32 else intercept_f64) diff --git a/python/cuml/cuml/solvers/cd.pyx b/python/cuml/cuml/solvers/cd.pyx index 33f3de161c..e1440d2c07 100644 --- a/python/cuml/cuml/solvers/cd.pyx +++ b/python/cuml/cuml/solvers/cd.pyx @@ -6,7 +6,7 @@ import numpy as np from cuml.common import CumlArray from cuml.common.array_descriptor import CumlArrayDescriptor from cuml.common.doc_utils import generate_docstring -from cuml.internals.base import Base +from cuml.internals.base import Base, get_handle from cuml.internals.input_utils import input_to_cuml_array from cuml.internals.mixins import FMajorInputTagMixin from cuml.internals.outputs import reflect @@ -114,6 +114,9 @@ def fit_coordinate_descent( n_iter : int The number of iterations the solver ran for. """ + if handle is None: + handle = get_handle() + # Process and validate parameters if loss != "squared_loss": raise ValueError(f"{loss=!r} is not supported") @@ -351,7 +354,7 @@ class CD(Base, FMajorInputTagMixin): max_iter=self.max_iter, tol=self.tol, shuffle=self.shuffle, - handle=self.handle, + handle=get_handle(model=self), ) self.coef_ = coef self.intercept_ = intercept @@ -382,7 +385,8 @@ class CD(Base, FMajorInputTagMixin): cdef uintptr_t preds_ptr = preds.ptr cdef uintptr_t coef_ptr = self.coef_.ptr cdef double intercept = self.intercept_ - cdef handle_t* handle_ = self.handle.getHandle() + handle = get_handle(model=self) + cdef handle_t* handle_ = handle.getHandle() cdef bool is_float32 = self.coef_.dtype == np.float32 with nogil: @@ -408,6 +412,6 @@ class CD(Base, FMajorInputTagMixin): preds_ptr, 0, ) - self.handle.sync() + handle.sync() return preds diff --git a/python/cuml/cuml/solvers/cd_mg.pyx b/python/cuml/cuml/solvers/cd_mg.pyx index 413626c31d..a0b8d13265 100644 --- a/python/cuml/cuml/solvers/cd_mg.pyx +++ b/python/cuml/cuml/solvers/cd_mg.pyx @@ -4,7 +4,7 @@ # import numpy as np -from cuml.internals import run_in_internal_context +from cuml.internals import get_handle, run_in_internal_context from cuml.linear_model.base_mg import MGFitMixin from cuml.solvers import CD @@ -61,7 +61,8 @@ class CDMG(MGFitMixin, CD): """ @run_in_internal_context def _fit(self, uintptr_t X, uintptr_t y, uintptr_t coef_ptr, uintptr_t input_desc): - cdef handle_t* handle_ = self.handle.getHandle() + handle = get_handle(model=self) + cdef handle_t* handle_ = handle.getHandle() cdef bool use_f32 = self.dtype == np.float32 cdef bool fit_intercept = self.fit_intercept cdef int max_iter = self.max_iter @@ -106,7 +107,7 @@ class CDMG(MGFitMixin, CD): tol, False ) - self.handle.sync() + handle.sync() self.intercept_ = intercept_f32 if use_f32 else intercept_f64 self.n_iter_ = n_iter diff --git a/python/cuml/cuml/solvers/qn.pyx b/python/cuml/cuml/solvers/qn.pyx index f32d29a969..2ee39757e3 100644 --- a/python/cuml/cuml/solvers/qn.pyx +++ b/python/cuml/cuml/solvers/qn.pyx @@ -3,7 +3,6 @@ # import cupy as cp import numpy as np -from pylibraft.common.handle import Handle from cuml.common import input_to_cuml_array from cuml.common.array_descriptor import CumlArrayDescriptor @@ -11,7 +10,7 @@ from cuml.common.doc_utils import generate_docstring from cuml.common.sparse_utils import is_sparse from cuml.internals.array import CumlArray from cuml.internals.array_sparse import SparseCumlArray -from cuml.internals.base import Base +from cuml.internals.base import Base, get_handle from cuml.internals.outputs import reflect, run_in_internal_context from cuml.metrics import accuracy_score @@ -165,7 +164,7 @@ def fit_qn( The value of the objective function. """ if handle is None: - handle = Handle() + handle = get_handle() cdef bool sparse_X = is_sparse(X) cdef int n_rows, n_cols @@ -564,7 +563,7 @@ class QN(Base): penalty_normalized=self.penalty_normalized, init_coef=init_coef, verbose=self._verbose_level, - handle=self.handle, + handle=get_handle(model=self), ) self.coef_ = coef self.intercept_ = intercept diff --git a/python/cuml/cuml/solvers/sgd.pyx b/python/cuml/cuml/solvers/sgd.pyx index b66c2f98b3..6aee2de646 100644 --- a/python/cuml/cuml/solvers/sgd.pyx +++ b/python/cuml/cuml/solvers/sgd.pyx @@ -1,13 +1,12 @@ # SPDX-FileCopyrightText: Copyright (c) 2018-2025, NVIDIA CORPORATION. # SPDX-License-Identifier: Apache-2.0 import numpy as np -from pylibraft.common.handle import Handle from cuml.common import input_to_cuml_array from cuml.common.array_descriptor import CumlArrayDescriptor from cuml.common.doc_utils import generate_docstring from cuml.internals.array import CumlArray -from cuml.internals.base import Base +from cuml.internals.base import Base, get_handle from cuml.internals.mixins import FMajorInputTagMixin from cuml.internals.outputs import reflect @@ -186,7 +185,7 @@ def fit_sgd( # Perform fit if handle is None: - handle = Handle() + handle = get_handle() cdef handle_t* handle_ = handle.getHandle() cdef uintptr_t X_ptr = X.ptr cdef uintptr_t y_ptr = y.ptr @@ -425,7 +424,7 @@ class SGD(Base, FMajorInputTagMixin): power_t=self.power_t, batch_size=self.batch_size, n_iter_no_change=self.n_iter_no_change, - handle=self.handle, + handle=get_handle(model=self), ) self.coef_ = coef self.intercept_ = intercept @@ -455,7 +454,8 @@ class SGD(Base, FMajorInputTagMixin): preds = CumlArray.zeros(n_rows, dtype=self.coef_.dtype, index=X.index) - cdef handle_t* handle_ = self.handle.getHandle() + handle = get_handle(model=self) + cdef handle_t* handle_ = handle.getHandle() cdef int loss_code = _LOSSES[self.loss] cdef bool use_f32 = self.coef_.dtype == np.float32 cdef uintptr_t preds_ptr = preds.ptr @@ -486,19 +486,6 @@ class SGD(Base, FMajorInputTagMixin): preds_ptr, loss_code, ) - self.handle.sync() + handle.sync() return preds - - def predictClass(self, X, convert_dtype=True): - """This method has been removed. - - Instead use ``sgd.predict() > 0.5`` for ``loss="hinge"`` and - ``sgd.predict() > 0`` otherwise. For actual classifier support - please use ``MBSGDClassifier`` instead. - """ - raise NotImplementedError( - "This method was removed in 25.12 as a breaking change.\n\n" - "Please use ``sgd.predict() > 0.5`` for ``loss='hinge'`` and " - "``sgd.predict() > 0`` otherwise." - ) From cd65a23590dfb39150aabc0c5ff0d5373022bf83 Mon Sep 17 00:00:00 2001 From: Jim Crist-Harif Date: Wed, 17 Dec 2025 16:11:36 -0600 Subject: [PATCH 08/21] Use `get_handle` in `cuml.manifold` --- .../cuml/cuml/manifold/spectral_embedding.pyx | 12 +++----- python/cuml/cuml/manifold/t_sne.pyx | 7 +++-- python/cuml/cuml/manifold/umap/umap.pyx | 30 +++++++++++-------- python/cuml/tests/test_umap.py | 11 ++----- 4 files changed, 29 insertions(+), 31 deletions(-) diff --git a/python/cuml/cuml/manifold/spectral_embedding.pyx b/python/cuml/cuml/manifold/spectral_embedding.pyx index 0646eb33fa..7343290ed2 100644 --- a/python/cuml/cuml/manifold/spectral_embedding.pyx +++ b/python/cuml/cuml/manifold/spectral_embedding.pyx @@ -6,11 +6,10 @@ import cupy as cp import cupyx.scipy.sparse as cp_sp import numpy as np import scipy.sparse as sp -from pylibraft.common.handle import Handle from cuml.common.array_descriptor import CumlArrayDescriptor from cuml.internals.array import CumlArray -from cuml.internals.base import Base +from cuml.internals.base import Base, get_handle from cuml.internals.input_utils import input_to_cupy_array from cuml.internals.interop import ( InteropMixin, @@ -139,8 +138,7 @@ def spectral_embedding(A, >>> embedding.shape (100, 2) """ - if handle is None: - handle = Handle() + handle = get_handle(handle=handle) cdef float* affinity_data_ptr = NULL cdef int* affinity_rows_ptr = NULL @@ -263,9 +261,7 @@ def spectral_embedding(A, return eigenvectors -class SpectralEmbedding(Base, - InteropMixin, - CMajorInputTagMixin): +class SpectralEmbedding(Base, InteropMixin, CMajorInputTagMixin): """Spectral embedding for non-linear dimensionality reduction. Forms an affinity matrix given by the specified function and @@ -445,7 +441,7 @@ class SpectralEmbedding(Base, affinity=self.affinity, random_state=self.random_state, n_neighbors=self.n_neighbors_, - handle=self.handle + handle=self.handle, ) return self diff --git a/python/cuml/cuml/manifold/t_sne.pyx b/python/cuml/cuml/manifold/t_sne.pyx index 95846a6d2c..bf05a25593 100644 --- a/python/cuml/cuml/manifold/t_sne.pyx +++ b/python/cuml/cuml/manifold/t_sne.pyx @@ -14,7 +14,7 @@ from cuml.common.sparse_utils import is_sparse from cuml.common.sparsefuncs import extract_knn_graph from cuml.internals.array import CumlArray from cuml.internals.array_sparse import SparseCumlArray -from cuml.internals.base import Base +from cuml.internals.base import Base, get_handle from cuml.internals.interop import ( InteropMixin, UnsupportedOnGPU, @@ -643,7 +643,8 @@ class TSNE(Base, cdef uintptr_t embed_ptr = embedding.ptr # Execute fit - cdef handle_t* handle_ = self.handle.getHandle() + handle = get_handle(model=self) + cdef handle_t* handle_ = handle.getHandle() cdef float kl_divergence = 0 cdef int n_iter = 0 @@ -677,7 +678,7 @@ class TSNE(Base, &kl_divergence, &n_iter, ) - self.handle.sync() + handle.sync() # Store fitted attributes self._kl_divergence_ = kl_divergence diff --git a/python/cuml/cuml/manifold/umap/umap.pyx b/python/cuml/cuml/manifold/umap/umap.pyx index b471dc8518..76466937f4 100644 --- a/python/cuml/cuml/manifold/umap/umap.pyx +++ b/python/cuml/cuml/manifold/umap/umap.pyx @@ -10,7 +10,6 @@ import cupyx.scipy.sparse import joblib import numpy as np import scipy.sparse -from pylibraft.common.handle import Handle from cuml.common.array_descriptor import CumlArrayDescriptor from cuml.common.doc_utils import generate_docstring @@ -19,7 +18,7 @@ from cuml.common.sparsefuncs import extract_knn_graph from cuml.internals import logger, reflect from cuml.internals.array import CumlArray from cuml.internals.array_sparse import SparseCumlArray -from cuml.internals.base import Base +from cuml.internals.base import Base, get_handle from cuml.internals.input_utils import input_to_cuml_array, is_array_like from cuml.internals.interop import ( InteropMixin, @@ -559,9 +558,6 @@ class UMAP(Base, InteropMixin, CMajorInputTagMixin, SparseInputTagMixin): run different models concurrently in different streams by creating handles in several streams. If it is None, a new one is created. - Using `pylibraft.common.DeviceResourcesSNMG` as the handle will run batched knn graph - building using multiple GPUs. This will only be valid when `build_algo=nn_descent` and - `nnd_n_clusters > 1`. verbose : int or boolean, default=False Sets logging level. It must be one of `cuml.common.logger.level_*`. See :ref:`verbosity-levels` for more info. @@ -616,6 +612,11 @@ class UMAP(Base, InteropMixin, CMajorInputTagMixin, SparseInputTagMixin): - Start with `nnd_n_clusters = 4` and increase (4 → 8 → 16...) for less GPU memory usage. This is independent from nnd_overlap_factor as long as 'nnd_overlap_factor' < 'nnd_n_clusters'. + device_ids : list[int], "all", or None, default=None + The device IDs to use during fitting (only used when + `build_algo=nn_descent` and `nnd_n_clusters > 1`). May be a list of + ids, ``"all"`` (to use all available devices), or ``None`` (to fit + using a single GPU only). Default is None. Notes ----- @@ -674,7 +675,8 @@ class UMAP(Base, InteropMixin, CMajorInputTagMixin, SparseInputTagMixin): "metric_kwds", "precomputed_knn", "build_algo", - "build_kwds" + "build_kwds", + "device_ids", ] @classmethod @@ -849,6 +851,7 @@ class UMAP(Base, InteropMixin, CMajorInputTagMixin, SparseInputTagMixin): callback=None, build_algo="auto", build_kwds=None, + device_ids=None, handle=None, verbose=False, output_type=None, @@ -880,6 +883,7 @@ class UMAP(Base, InteropMixin, CMajorInputTagMixin, SparseInputTagMixin): self.callback = callback self.build_algo = build_algo self.build_kwds = build_kwds + self.device_ids = device_ids @generate_docstring( convert_dtype_cast="np.float32", @@ -988,7 +992,8 @@ class UMAP(Base, InteropMixin, CMajorInputTagMixin, SparseInputTagMixin): else: knn_indices = knn_dists = None - cdef handle_t * handle_ = self.handle.getHandle() + handle = get_handle(model=self, device_ids=self.device_ids) + cdef handle_t * handle_ = handle.getHandle() cdef unique_ptr[device_buffer] embeddings_buffer cdef lib.HostCOO fss_graph = lib.HostCOO() @@ -1022,7 +1027,7 @@ class UMAP(Base, InteropMixin, CMajorInputTagMixin, SparseInputTagMixin): embeddings_buffer, fss_graph, ) - self.handle.sync() + handle.sync() buffer = DeviceBuffer.c_from_unique_ptr(move(embeddings_buffer)) embedding = cp.ndarray( @@ -1188,7 +1193,8 @@ class UMAP(Base, InteropMixin, CMajorInputTagMixin, SparseInputTagMixin): cdef uintptr_t out_ptr = out.ptr cdef uintptr_t embedding_ptr = self.embedding_.ptr - cdef handle_t* handle_ = self.handle.getHandle() + handle = get_handle(model=self, device_ids=self.device_ids) + cdef handle_t* handle_ = handle.getHandle() with nogil: if X_is_sparse: @@ -1223,7 +1229,7 @@ class UMAP(Base, InteropMixin, CMajorInputTagMixin, SparseInputTagMixin): ¶ms, out_ptr ) - self.handle.sync() + handle.sync() return out @@ -1345,7 +1351,7 @@ def fuzzy_simplicial_set( knn_indices_ptr = 0 knn_dists_ptr = 0 - handle = Handle() + handle = get_handle() cdef handle_t* handle_ = handle.getHandle() cdef unique_ptr[lib.COO] fss_graph_ptr = lib.get_graph( handle_[0], @@ -1509,7 +1515,7 @@ def simplicial_set_embedding( if not isinstance(graph, cupyx.scipy.sparse.coo_matrix): graph = cupyx.scipy.sparse.coo_matrix(graph) - handle = Handle() + handle = get_handle() cdef handle_t* handle_ = handle.getHandle() cdef RaftCOO fss_graph = RaftCOO.from_cupy_coo(handle, graph) cdef uintptr_t embedding_ptr = embedding.ptr diff --git a/python/cuml/tests/test_umap.py b/python/cuml/tests/test_umap.py index 6b7841b8b5..a62ed1480d 100644 --- a/python/cuml/tests/test_umap.py +++ b/python/cuml/tests/test_umap.py @@ -12,7 +12,6 @@ import sklearn import umap from packaging.version import Version -from pylibraft.common import DeviceResourcesSNMG from sklearn import datasets from sklearn.cluster import KMeans from sklearn.datasets import make_blobs, make_moons @@ -853,23 +852,19 @@ def test_umap_distance_metrics_fit_transform_trust_on_sparse_input( @pytest.mark.parametrize("num_clusters", [3, 5]) @pytest.mark.parametrize("fit_then_transform", [False, True]) @pytest.mark.parametrize("metric", ["l2", "sqeuclidean", "cosine"]) -@pytest.mark.parametrize("do_snmg", [True, False]) +@pytest.mark.parametrize("device_ids", [None, "all"]) def test_umap_trustworthiness_on_batch_nnd( - num_clusters, fit_then_transform, metric, do_snmg + num_clusters, fit_then_transform, metric, device_ids ): digits = datasets.load_digits() - umap_handle = None - if do_snmg: - umap_handle = DeviceResourcesSNMG() - cuml_model = cuUMAP( - handle=umap_handle, n_neighbors=10, min_dist=0.01, build_algo="nn_descent", build_kwds={"nnd_n_clusters": num_clusters}, metric=metric, + device_ids=device_ids, ) if fit_then_transform: From cb76f9f9c28dc5f6a8755090dd17047f58bbfbad Mon Sep 17 00:00:00 2001 From: Jim Crist-Harif Date: Thu, 18 Dec 2025 11:15:20 -0600 Subject: [PATCH 09/21] Use `get_handle` in `cuml.metrics` --- .../metrics/cluster/adjusted_rand_index.pyx | 8 +- .../metrics/cluster/completeness_score.pyx | 9 +- python/cuml/cuml/metrics/cluster/entropy.pyx | 4 +- .../metrics/cluster/homogeneity_score.pyx | 9 +- .../metrics/cluster/mutual_info_score.pyx | 9 +- .../cuml/metrics/cluster/silhouette_score.pyx | 4 +- .../cuml/cuml/metrics/cluster/v_measure.pyx | 9 +- python/cuml/cuml/metrics/kl_divergence.pyx | 4 +- .../cuml/cuml/metrics/pairwise_distances.pyx | 22 ++-- python/cuml/cuml/metrics/trustworthiness.pyx | 6 +- python/cuml/cuml/testing/utils.py | 16 --- python/cuml/tests/test_metrics.py | 123 +++++++----------- 12 files changed, 82 insertions(+), 141 deletions(-) diff --git a/python/cuml/cuml/metrics/cluster/adjusted_rand_index.pyx b/python/cuml/cuml/metrics/cluster/adjusted_rand_index.pyx index 1e912fd911..5a4e3a272d 100644 --- a/python/cuml/cuml/metrics/cluster/adjusted_rand_index.pyx +++ b/python/cuml/cuml/metrics/cluster/adjusted_rand_index.pyx @@ -3,9 +3,9 @@ # SPDX-License-Identifier: Apache-2.0 # import cupy as cp -from pylibraft.common.handle import Handle from cuml.common import input_to_cuml_array +from cuml.internals import get_handle from libc.stdint cimport uintptr_t from pylibraft.common.handle cimport handle_t @@ -38,10 +38,8 @@ def adjusted_rand_score(labels_true, labels_pred, handle=None, float The adjusted rand index value between -1.0 and 1.0 """ - handle = Handle() \ - if handle is None else handle - cdef handle_t* handle_ =\ - handle.getHandle() + handle = get_handle(handle=handle) + cdef handle_t* handle_ = handle.getHandle() labels_true, n_rows, _, _ = \ input_to_cuml_array(labels_true, order='C', check_dtype=cp.int32, diff --git a/python/cuml/cuml/metrics/cluster/completeness_score.pyx b/python/cuml/cuml/metrics/cluster/completeness_score.pyx index a5eeafb16c..abebe88a25 100644 --- a/python/cuml/cuml/metrics/cluster/completeness_score.pyx +++ b/python/cuml/cuml/metrics/cluster/completeness_score.pyx @@ -2,13 +2,12 @@ # SPDX-FileCopyrightText: Copyright (c) 2020-2025, NVIDIA CORPORATION. # SPDX-License-Identifier: Apache-2.0 # +from cuml.internals import get_handle +from cuml.metrics.cluster.utils import prepare_cluster_metric_inputs + from libc.stdint cimport uintptr_t from pylibraft.common.handle cimport handle_t -from pylibraft.common.handle import Handle - -from cuml.metrics.cluster.utils import prepare_cluster_metric_inputs - cdef extern from "cuml/metrics/metrics.hpp" namespace "ML::Metrics" nogil: double completeness_score(const handle_t & handle, const int *y, @@ -59,7 +58,7 @@ def cython_completeness_score(labels_true, labels_pred, handle=None) -> float: The completeness of the predicted labeling given the ground truth. Score between 0.0 and 1.0. 1.0 stands for perfectly complete labeling. """ - handle = Handle() if handle is None else handle + handle = get_handle(handle=handle) cdef handle_t *handle_ = handle.getHandle() (y_true, y_pred, n_rows, diff --git a/python/cuml/cuml/metrics/cluster/entropy.pyx b/python/cuml/cuml/metrics/cluster/entropy.pyx index 1e444c6c9c..2696ceee32 100644 --- a/python/cuml/cuml/metrics/cluster/entropy.pyx +++ b/python/cuml/cuml/metrics/cluster/entropy.pyx @@ -6,8 +6,8 @@ import math import cupy as cp import numpy as np -from pylibraft.common.handle import Handle +from cuml.internals import get_handle from cuml.internals.input_utils import input_to_cupy_array from libc.stdint cimport uintptr_t @@ -48,7 +48,7 @@ def cython_entropy(clustering, base=None, handle=None) -> float: S : float The calculated entropy. """ - handle = Handle() if handle is None else handle + handle = get_handle(handle=handle) cdef handle_t *handle_ = handle.getHandle() clustering, n_rows, _, _ = input_to_cupy_array( diff --git a/python/cuml/cuml/metrics/cluster/homogeneity_score.pyx b/python/cuml/cuml/metrics/cluster/homogeneity_score.pyx index bcdf23dc43..d8892c52b7 100644 --- a/python/cuml/cuml/metrics/cluster/homogeneity_score.pyx +++ b/python/cuml/cuml/metrics/cluster/homogeneity_score.pyx @@ -2,13 +2,12 @@ # SPDX-FileCopyrightText: Copyright (c) 2020-2025, NVIDIA CORPORATION. # SPDX-License-Identifier: Apache-2.0 # +from cuml.internals import get_handle +from cuml.metrics.cluster.utils import prepare_cluster_metric_inputs + from libc.stdint cimport uintptr_t from pylibraft.common.handle cimport handle_t -from pylibraft.common.handle import Handle - -from cuml.metrics.cluster.utils import prepare_cluster_metric_inputs - cdef extern from "cuml/metrics/metrics.hpp" namespace "ML::Metrics" nogil: double homogeneity_score(const handle_t & handle, const int *y, @@ -59,7 +58,7 @@ def cython_homogeneity_score(labels_true, labels_pred, handle=None) -> float: The homogeneity of the predicted labeling given the ground truth. Score between 0.0 and 1.0. 1.0 stands for perfectly homogeneous labeling. """ - handle = Handle() if handle is None else handle + handle = get_handle(handle=handle) cdef handle_t *handle_ = handle.getHandle() (y_true, y_pred, diff --git a/python/cuml/cuml/metrics/cluster/mutual_info_score.pyx b/python/cuml/cuml/metrics/cluster/mutual_info_score.pyx index f438d69088..107fc5608d 100644 --- a/python/cuml/cuml/metrics/cluster/mutual_info_score.pyx +++ b/python/cuml/cuml/metrics/cluster/mutual_info_score.pyx @@ -2,13 +2,12 @@ # SPDX-FileCopyrightText: Copyright (c) 2020-2025, NVIDIA CORPORATION. # SPDX-License-Identifier: Apache-2.0 # +from cuml.internals import get_handle +from cuml.metrics.cluster.utils import prepare_cluster_metric_inputs + from libc.stdint cimport uintptr_t from pylibraft.common.handle cimport handle_t -from pylibraft.common.handle import Handle - -from cuml.metrics.cluster.utils import prepare_cluster_metric_inputs - cdef extern from "cuml/metrics/metrics.hpp" namespace "ML::Metrics" nogil: double mutual_info_score(const handle_t &handle, @@ -56,7 +55,7 @@ def cython_mutual_info_score(labels_true, labels_pred, handle=None) -> float: float Mutual information, a non-negative value """ - handle = Handle() if handle is None else handle + handle = get_handle(handle=handle) cdef handle_t *handle_ = handle.getHandle() (y_true, y_pred, n_rows, diff --git a/python/cuml/cuml/metrics/cluster/silhouette_score.pyx b/python/cuml/cuml/metrics/cluster/silhouette_score.pyx index 5b6fcb68cc..8aaf910ca4 100644 --- a/python/cuml/cuml/metrics/cluster/silhouette_score.pyx +++ b/python/cuml/cuml/metrics/cluster/silhouette_score.pyx @@ -4,9 +4,9 @@ # import cupy as cp import numpy as np -from pylibraft.common.handle import Handle from cuml.common import input_to_cuml_array +from cuml.internals import get_handle from cuml.metrics.pairwise_distances import _determine_metric from cuml.prims.label.classlabels import check_labels, make_monotonic @@ -74,7 +74,7 @@ def _silhouette_coeff( handles in several streams. If it is None, a new one is created. """ - handle = Handle() if handle is None else handle + handle = get_handle(handle=handle) cdef handle_t *handle_ = handle.getHandle() if chunksize is None: diff --git a/python/cuml/cuml/metrics/cluster/v_measure.pyx b/python/cuml/cuml/metrics/cluster/v_measure.pyx index 7b85d9e636..aed96d6892 100644 --- a/python/cuml/cuml/metrics/cluster/v_measure.pyx +++ b/python/cuml/cuml/metrics/cluster/v_measure.pyx @@ -2,13 +2,12 @@ # SPDX-FileCopyrightText: Copyright (c) 2022-2025, NVIDIA CORPORATION. # SPDX-License-Identifier: Apache-2.0 # +from cuml.internals import get_handle +from cuml.metrics.cluster.utils import prepare_cluster_metric_inputs + from libc.stdint cimport uintptr_t from pylibraft.common.handle cimport handle_t -from pylibraft.common.handle import Handle - -from cuml.metrics.cluster.utils import prepare_cluster_metric_inputs - cdef extern from "cuml/metrics/metrics.hpp" namespace "ML::Metrics" nogil: double v_measure(const handle_t & handle, @@ -66,7 +65,7 @@ def cython_v_measure(labels_true, labels_pred, beta=1.0, handle=None) -> float: v_measure_value : float score between 0.0 and 1.0. 1.0 stands for perfectly complete labeling """ - handle = Handle() if handle is None else handle + handle = get_handle(handle=handle) cdef handle_t *handle_ = handle.getHandle() (y_true, y_pred, n_rows, diff --git a/python/cuml/cuml/metrics/kl_divergence.pyx b/python/cuml/cuml/metrics/kl_divergence.pyx index 3e9d0b320f..4373a1c528 100644 --- a/python/cuml/cuml/metrics/kl_divergence.pyx +++ b/python/cuml/cuml/metrics/kl_divergence.pyx @@ -3,9 +3,9 @@ # SPDX-License-Identifier: Apache-2.0 # import numpy as np -from pylibraft.common.handle import Handle from cuml.common import input_to_cuml_array +from cuml.internals import get_handle from libc.stdint cimport uintptr_t from pylibraft.common.handle cimport handle_t @@ -56,7 +56,7 @@ def kl_divergence(P, Q, handle=None, convert_dtype=True): float The KL Divergence value """ - handle = Handle() if handle is None else handle + handle = get_handle(handle=handle) cdef handle_t *handle_ = handle.getHandle() P_m, n_features_p, _, dtype_p = \ diff --git a/python/cuml/cuml/metrics/pairwise_distances.pyx b/python/cuml/cuml/metrics/pairwise_distances.pyx index 8e457bee6b..5c84fec0af 100644 --- a/python/cuml/cuml/metrics/pairwise_distances.pyx +++ b/python/cuml/cuml/metrics/pairwise_distances.pyx @@ -4,26 +4,24 @@ # import warnings -from libc.stdint cimport uintptr_t -from libcpp cimport bool -from pylibraft.common.handle cimport handle_t - import cudf import cupy as cp import numpy as np import pandas as pd import scipy.sparse -from pylibraft.common.handle import Handle -import cuml.internals from cuml.common import CumlArray, input_to_cuml_array from cuml.common.sparse_utils import is_sparse +from cuml.internals import get_handle, reflect from cuml.internals.array_sparse import SparseCumlArray from cuml.internals.input_utils import sparse_scipy_to_cp +from cuml.thirdparty_adapters import _get_mask -from cuml.metrics.distance_type cimport DistanceType +from libc.stdint cimport uintptr_t +from libcpp cimport bool +from pylibraft.common.handle cimport handle_t -from cuml.thirdparty_adapters import _get_mask +from cuml.metrics.distance_type cimport DistanceType cdef extern from "cuml/metrics/metrics.hpp" namespace "ML::Metrics" nogil: @@ -247,7 +245,7 @@ def nan_euclidean_distances( return distances -@cuml.internals.reflect +@reflect def pairwise_distances(X, Y=None, metric="euclidean", handle=None, convert_dtype=True, metric_arg=2, **kwds): """ @@ -330,7 +328,7 @@ def pairwise_distances(X, Y=None, metric="euclidean", handle=None, return sparse_pairwise_distances(X, Y, metric, handle, convert_dtype, **kwds) - handle = Handle() if handle is None else handle + handle = get_handle(handle=handle) cdef handle_t *handle_ = handle.getHandle() if metric in ['nan_euclidean']: @@ -438,7 +436,7 @@ def pairwise_distances(X, Y=None, metric="euclidean", handle=None, return dest_m -@cuml.internals.reflect +@reflect def sparse_pairwise_distances(X, Y=None, metric="euclidean", handle=None, convert_dtype=True, metric_arg=2, **kwds): """ @@ -522,7 +520,7 @@ def sparse_pairwise_distances(X, Y=None, metric="euclidean", handle=None, array([[3.], [2.]]) """ - handle = Handle() if handle is None else handle + handle = get_handle(handle=handle) cdef handle_t *handle_ = handle.getHandle() if (not is_sparse(X)) or (Y is not None and not is_sparse(Y)): raise ValueError("Input matrices are not sparse.") diff --git a/python/cuml/cuml/metrics/trustworthiness.pyx b/python/cuml/cuml/metrics/trustworthiness.pyx index 9259de1038..34701644bd 100644 --- a/python/cuml/cuml/metrics/trustworthiness.pyx +++ b/python/cuml/cuml/metrics/trustworthiness.pyx @@ -3,8 +3,8 @@ # SPDX-License-Identifier: Apache-2.0 # import numpy as np -from pylibraft.common.handle import Handle +from cuml.internals import get_handle from cuml.internals.input_utils import input_to_cuml_array from libc.stdint cimport uintptr_t @@ -78,8 +78,6 @@ def trustworthiness(X, X_embedded, handle=None, n_neighbors=5, if n_neighbors > X.shape[0]: raise ValueError("n_neighbors must be <= the number of rows.") - handle = Handle() if handle is None else handle - cdef uintptr_t d_X_ptr cdef uintptr_t d_X_embedded_ptr @@ -96,7 +94,7 @@ def trustworthiness(X, X_embedded, handle=None, n_neighbors=5, else None)) d_X_embedded_ptr = X_m2.ptr - handle = Handle() if handle is None else handle + handle = get_handle(handle=handle) cdef handle_t* handle_ = handle.getHandle() if metric == 'euclidean': diff --git a/python/cuml/cuml/testing/utils.py b/python/cuml/cuml/testing/utils.py index d615c7a570..762524f535 100644 --- a/python/cuml/cuml/testing/utils.py +++ b/python/cuml/cuml/testing/utils.py @@ -406,22 +406,6 @@ def generate_random_labels(random_generation_lambda, seed=1234, as_cupy=False): return cuda.to_device(a), cuda.to_device(b), a, b -def score_labeling_with_handle( - func, ground_truth, predictions, use_handle, dtype=np.int32 -): - """Test helper to standardize inputs between sklearn and our prims metrics. - - Using this function we can pass python lists as input of a test just like - with sklearn as well as an option to use handle with our metrics. - """ - a = cp.array(ground_truth, dtype=dtype) - b = cp.array(predictions, dtype=dtype) - - handle, stream = get_handle(use_handle) - - return func(a, b, handle=handle) - - def get_number_positional_args(func, default=2): # function to return number of positional arguments in func if hasattr(func, "__code__"): diff --git a/python/cuml/tests/test_metrics.py b/python/cuml/tests/test_metrics.py index faa47faeac..2a962a5721 100644 --- a/python/cuml/tests/test_metrics.py +++ b/python/cuml/tests/test_metrics.py @@ -66,9 +66,7 @@ from cuml.testing.utils import ( array_equal, generate_random_labels, - get_handle, quality_param, - score_labeling_with_handle, stress_param, unit_param, ) @@ -376,93 +374,75 @@ def test_silhouette_score_batched_non_monotonic(): assert array_equal(cuml_samples, sk_samples) -def score_homogeneity(ground_truth, predictions, use_handle): - return score_labeling_with_handle( - cuml.metrics.homogeneity_score, - ground_truth, - predictions, - use_handle, - dtype=np.int32, - ) +def score_homogeneity(ground_truth, predictions): + a = cp.array(ground_truth, dtype=np.int32) + b = cp.array(predictions, dtype=np.int32) + return cuml.metrics.homogeneity_score(a, b) -def score_completeness(ground_truth, predictions, use_handle): - return score_labeling_with_handle( - cuml.metrics.completeness_score, - ground_truth, - predictions, - use_handle, - dtype=np.int32, - ) +def score_completeness(ground_truth, predictions): + a = cp.array(ground_truth, dtype=np.int32) + b = cp.array(predictions, dtype=np.int32) + return cuml.metrics.completeness_score(a, b) -def score_mutual_info(ground_truth, predictions, use_handle): - return score_labeling_with_handle( - cuml.metrics.mutual_info_score, - ground_truth, - predictions, - use_handle, - dtype=np.int32, - ) +def score_mutual_info(ground_truth, predictions): + a = cp.array(ground_truth, dtype=np.int32) + b = cp.array(predictions, dtype=np.int32) + return cuml.metrics.mutual_info_score(a, b) -@pytest.mark.parametrize("use_handle", [True, False]) @pytest.mark.parametrize( "data", [([0, 0, 1, 1], [1, 1, 0, 0]), ([0, 0, 1, 1], [0, 0, 1, 1])] ) -def test_homogeneity_perfect_labeling(use_handle, data): +def test_homogeneity_perfect_labeling(data): # Perfect labelings are homogeneous - hom = score_homogeneity(*data, use_handle) + hom = score_homogeneity(*data) assert_almost_equal(hom, 1.0, decimal=4) -@pytest.mark.parametrize("use_handle", [True, False]) @pytest.mark.parametrize( "data", [([0, 0, 1, 1], [0, 0, 1, 2]), ([0, 0, 1, 1], [0, 1, 2, 3])] ) -def test_homogeneity_non_perfect_labeling(use_handle, data): +def test_homogeneity_non_perfect_labeling(data): # Non-perfect labelings that further split classes into more clusters can # be perfectly homogeneous - hom = score_homogeneity(*data, use_handle) + hom = score_homogeneity(*data) assert_almost_equal(hom, 1.0, decimal=4) -@pytest.mark.parametrize("use_handle", [True, False]) @pytest.mark.parametrize( "data", [([0, 0, 1, 1], [0, 1, 0, 1]), ([0, 0, 1, 1], [0, 0, 0, 0])] ) -def test_homogeneity_non_homogeneous_labeling(use_handle, data): +def test_homogeneity_non_homogeneous_labeling(data): # Clusters that include samples from different classes do not make for an # homogeneous labeling - hom = score_homogeneity(*data, use_handle) + hom = score_homogeneity(*data) assert_almost_equal(hom, 0.0, decimal=4) -@pytest.mark.parametrize("use_handle", [True, False]) @pytest.mark.parametrize("input_range", [[0, 1000], [-1000, 1000]]) -def test_homogeneity_score_big_array(use_handle, input_range): +def test_homogeneity_score_big_array(input_range): a, b, _, _ = generate_random_labels( lambda rd: rd.randint(*input_range, int(10e4), dtype=np.int32) ) - score = score_homogeneity(a, b, use_handle) + score = score_homogeneity(a, b) ref = sk_homogeneity_score(a, b) np.testing.assert_almost_equal(score, ref, decimal=4) -@pytest.mark.parametrize("use_handle", [True, False]) @pytest.mark.parametrize( "input_range", [[0, 2], [-5, 20], [int(-10e2), int(10e2)]] ) -def test_homogeneity_completeness_symmetry(use_handle, input_range): +def test_homogeneity_completeness_symmetry(input_range): a, b, _, _ = generate_random_labels( lambda rd: rd.randint(*input_range, int(10e3), dtype=np.int32) ) - hom = score_homogeneity(a, b, use_handle) - com = score_completeness(b, a, use_handle) + hom = score_homogeneity(a, b) + com = score_completeness(b, a) np.testing.assert_almost_equal(hom, com, decimal=4) -@pytest.mark.parametrize("use_handle", [True, False]) @pytest.mark.parametrize( "input_labels", [ @@ -474,86 +454,79 @@ def test_homogeneity_completeness_symmetry(use_handle, input_range): ([0, 0, 1, 1], [0, 0, 0, 0]), ], ) -def test_mutual_info_score(use_handle, input_labels): - score = score_mutual_info(*input_labels, use_handle) +def test_mutual_info_score(input_labels): + score = score_mutual_info(*input_labels) ref = sk_mutual_info_score(*input_labels) np.testing.assert_almost_equal(score, ref, decimal=4) -@pytest.mark.parametrize("use_handle", [True, False]) @pytest.mark.parametrize("input_range", [[0, 1000], [-1000, 1000]]) -def test_mutual_info_score_big_array(use_handle, input_range): +def test_mutual_info_score_big_array(input_range): a, b, _, _ = generate_random_labels( lambda rd: rd.randint(*input_range, int(10e4), dtype=np.int32) ) - score = score_mutual_info(a, b, use_handle) + score = score_mutual_info(a, b) ref = sk_mutual_info_score(a, b) np.testing.assert_almost_equal(score, ref, decimal=4) -@pytest.mark.parametrize("use_handle", [True, False]) @pytest.mark.parametrize("n", [14]) -def test_mutual_info_score_range_equal_samples(use_handle, n): +def test_mutual_info_score_range_equal_samples(n): input_range = (-n, n) a, b, _, _ = generate_random_labels( lambda rd: rd.randint(*input_range, n, dtype=np.int32) ) - score = score_mutual_info(a, b, use_handle) + score = score_mutual_info(a, b) ref = sk_mutual_info_score(a, b) np.testing.assert_almost_equal(score, ref, decimal=4) -@pytest.mark.parametrize("use_handle", [True, False]) @pytest.mark.parametrize("input_range", [[0, 19], [0, 2], [-5, 20]]) @pytest.mark.parametrize("n_samples", [129, 258]) -def test_mutual_info_score_many_blocks(use_handle, input_range, n_samples): +def test_mutual_info_score_many_blocks(input_range, n_samples): a, b, _, _ = generate_random_labels( lambda rd: rd.randint(*input_range, n_samples, dtype=np.int32) ) - score = score_mutual_info(a, b, use_handle) + score = score_mutual_info(a, b) ref = sk_mutual_info_score(a, b) np.testing.assert_almost_equal(score, ref, decimal=4) -@pytest.mark.parametrize("use_handle", [True, False]) @pytest.mark.parametrize( "data", [([0, 0, 1, 1], [1, 1, 0, 0]), ([0, 0, 1, 1], [0, 0, 1, 1])] ) -def test_completeness_perfect_labeling(use_handle, data): +def test_completeness_perfect_labeling(data): # Perfect labelings are complete - com = score_completeness(*data, use_handle) + com = score_completeness(*data) np.testing.assert_almost_equal(com, 1.0, decimal=4) -@pytest.mark.parametrize("use_handle", [True, False]) @pytest.mark.parametrize( "data", [([0, 0, 1, 1], [0, 0, 0, 0]), ([0, 1, 2, 3], [0, 0, 1, 1])] ) -def test_completeness_non_perfect_labeling(use_handle, data): +def test_completeness_non_perfect_labeling(data): # Non-perfect labelings that assign all classes members to the same # clusters are still complete - com = score_completeness(*data, use_handle) + com = score_completeness(*data) np.testing.assert_almost_equal(com, 1.0, decimal=4) -@pytest.mark.parametrize("use_handle", [True, False]) @pytest.mark.parametrize( "data", [([0, 0, 1, 1], [0, 1, 0, 1]), ([0, 0, 0, 0], [0, 1, 2, 3])] ) -def test_completeness_non_complete_labeling(use_handle, data): +def test_completeness_non_complete_labeling(data): # If classes members are split across different clusters, the assignment # cannot be complete - com = score_completeness(*data, use_handle) + com = score_completeness(*data) np.testing.assert_almost_equal(com, 0.0, decimal=4) -@pytest.mark.parametrize("use_handle", [True, False]) @pytest.mark.parametrize("input_range", [[0, 1000], [-1000, 1000]]) -def test_completeness_score_big_array(use_handle, input_range): +def test_completeness_score_big_array(input_range): a, b, _, _ = generate_random_labels( lambda rd: rd.randint(*input_range, int(10e4), dtype=np.int32) ) - score = score_completeness(a, b, use_handle) + score = score_completeness(a, b) ref = sk_completeness_score(a, b) np.testing.assert_almost_equal(score, ref, decimal=4) @@ -767,28 +740,22 @@ def test_mean_squared_log_error_negative_values(inputs): ) -@pytest.mark.parametrize("use_handle", [True, False]) -def test_entropy(use_handle): - handle, stream = get_handle(use_handle) - +def test_entropy(): # The outcome of a fair coin is the most uncertain: # in base 2 the result is 1 (One bit of entropy). cluster = np.array([0, 1], dtype=np.int32) - assert_almost_equal(entropy(cluster, base=2.0, handle=handle), 1.0) + assert_almost_equal(entropy(cluster, base=2.0), 1.0) # The outcome of a biased coin is less uncertain: cluster = np.array(([0] * 9) + [1], dtype=np.int32) - assert_almost_equal(entropy(cluster, base=2.0, handle=handle), 0.468995593) + assert_almost_equal(entropy(cluster, base=2.0), 0.468995593) # base e - assert_almost_equal(entropy(cluster, handle=handle), 0.32508297339144826) + assert_almost_equal(entropy(cluster), 0.32508297339144826) @pytest.mark.parametrize("n_samples", [50, stress_param(500000)]) @pytest.mark.parametrize("base", [None, 2, 10, 50]) -@pytest.mark.parametrize("use_handle", [True, False]) -def test_entropy_random(n_samples, base, use_handle): - handle, stream = get_handle(use_handle) - +def test_entropy_random(n_samples, base): clustering, _, _, _ = generate_random_labels( lambda rng: rng.randint(0, 1000, n_samples) ) @@ -799,7 +766,7 @@ def test_entropy_random(n_samples, base, use_handle): # scipy's entropy uses probabilities sp_S = sp_entropy(pk, base=base) # we use a clustering - S = entropy(np.array(clustering, dtype=np.int32), base, handle=handle) + S = entropy(np.array(clustering, dtype=np.int32), base) assert_almost_equal(S, sp_S, decimal=2) From 87489e1131fc47322e297b7681d1aff3ed57028a Mon Sep 17 00:00:00 2001 From: Jim Crist-Harif Date: Thu, 18 Dec 2025 11:26:02 -0600 Subject: [PATCH 10/21] Use `get_handle` in `cuml.neighbors` --- .../cuml/neighbors/kneighbors_classifier.pyx | 11 +++++--- .../neighbors/kneighbors_classifier_mg.pyx | 12 +++++---- .../cuml/neighbors/kneighbors_regressor.pyx | 7 ++--- .../neighbors/kneighbors_regressor_mg.pyx | 7 ++--- .../cuml/cuml/neighbors/nearest_neighbors.pyx | 26 ++++++++++++------- .../cuml/neighbors/nearest_neighbors_mg.pyx | 7 ++--- 6 files changed, 42 insertions(+), 28 deletions(-) diff --git a/python/cuml/cuml/neighbors/kneighbors_classifier.pyx b/python/cuml/cuml/neighbors/kneighbors_classifier.pyx index 995acaaa2d..c3b0cc1df4 100644 --- a/python/cuml/cuml/neighbors/kneighbors_classifier.pyx +++ b/python/cuml/cuml/neighbors/kneighbors_classifier.pyx @@ -11,6 +11,7 @@ import cuml from cuml.common import input_to_cuml_array from cuml.common.classification import decode_labels, preprocess_labels from cuml.common.doc_utils import generate_docstring +from cuml.internals import get_handle from cuml.internals.array import CumlArray from cuml.internals.interop import UnsupportedOnGPU from cuml.internals.mixins import ClassifierMixin, FMajorInputTagMixin @@ -252,7 +253,8 @@ class KNeighborsClassifier(ClassifierMixin, 0 if weights_cp is None else weights_cp.data.ptr ) - cdef handle_t* handle_ = self.handle.getHandle() + handle = get_handle(model=self) + cdef handle_t* handle_ = handle.getHandle() cdef int64_t* inds_ptr = inds.ptr cdef size_t n_samples_fit = self._y.shape[0] cdef int n_neighbors = self.n_neighbors @@ -268,7 +270,7 @@ class KNeighborsClassifier(ClassifierMixin, weights_ptr ) - self.handle.sync() + handle.sync() with cuml.internals.exit_internal_context(): output_type = self._get_output_type(X) @@ -331,7 +333,8 @@ class KNeighborsClassifier(ClassifierMixin, 0 if weights_cp is None else weights_cp.data.ptr ) - cdef handle_t* handle_ = self.handle.getHandle() + handle = get_handle(model=self) + cdef handle_t* handle_ = handle.getHandle() cdef int64_t* inds_ptr = inds.ptr cdef size_t n_samples_fit = self._y.shape[0] cdef int n_neighbors = self.n_neighbors @@ -346,5 +349,5 @@ class KNeighborsClassifier(ClassifierMixin, n_neighbors, weights_ptr ) - self.handle.sync() + handle.sync() return probas[0] if len(probas) == 1 else probas diff --git a/python/cuml/cuml/neighbors/kneighbors_classifier_mg.pyx b/python/cuml/cuml/neighbors/kneighbors_classifier_mg.pyx index 39c917d5c4..530e28e9aa 100644 --- a/python/cuml/cuml/neighbors/kneighbors_classifier_mg.pyx +++ b/python/cuml/cuml/neighbors/kneighbors_classifier_mg.pyx @@ -5,7 +5,7 @@ import typing from cuml.common import input_to_cuml_array -from cuml.internals import logger, reflect +from cuml.internals import get_handle, logger, reflect from cuml.internals.array import CumlArray from cuml.neighbors.nearest_neighbors_mg import NearestNeighborsMG @@ -135,7 +135,8 @@ class KNeighborsClassifierMG(NearestNeighborsMG): out_result_local_parts.push_back(new intData_t( o_cai.ptr, n_rows * n_outputs)) - cdef handle_t* handle_ = self.handle.getHandle() + handle = get_handle(model=self) + cdef handle_t* handle_ = handle.getHandle() is_verbose = logger.should_log_for(logger.level_enum.debug) knn_classify( @@ -159,7 +160,7 @@ class KNeighborsClassifierMG(NearestNeighborsMG): is_verbose ) - self.handle.sync() + handle.sync() # Release memory type(self).free_mem(input) @@ -248,7 +249,8 @@ class KNeighborsClassifierMG(NearestNeighborsMG): probas_local_parts.at(query_idx).push_back( p_cai.ptr) - cdef handle_t* handle_ = self.handle.getHandle() + handle = get_handle(model=self) + cdef handle_t* handle_ = handle.getHandle() is_verbose = logger.should_log_for(logger.level_enum.debug) # Launch distributed operations @@ -272,7 +274,7 @@ class KNeighborsClassifierMG(NearestNeighborsMG): self.batch_size, is_verbose ) - self.handle.sync() + handle.sync() # Release memory type(self).free_mem(input) diff --git a/python/cuml/cuml/neighbors/kneighbors_regressor.pyx b/python/cuml/cuml/neighbors/kneighbors_regressor.pyx index 4719db56ae..aecbe74cdc 100644 --- a/python/cuml/cuml/neighbors/kneighbors_regressor.pyx +++ b/python/cuml/cuml/neighbors/kneighbors_regressor.pyx @@ -6,10 +6,10 @@ 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.mixins import FMajorInputTagMixin, RegressorMixin -from cuml.internals.outputs import reflect from cuml.neighbors.nearest_neighbors import NearestNeighbors from cuml.neighbors.weights import compute_weights @@ -246,7 +246,8 @@ class KNeighborsRegressor(RegressorMixin, FMajorInputTagMixin, NearestNeighbors) y_ptr = col.ptr y_vec.push_back(y_ptr) - cdef handle_t* handle_ = self.handle.getHandle() + handle = get_handle(model=self) + cdef handle_t* handle_ = handle.getHandle() # Compute weights (returns None for uniform weights) weights_cp = compute_weights(dists.to_output('cupy'), self.weights) @@ -268,6 +269,6 @@ class KNeighborsRegressor(RegressorMixin, FMajorInputTagMixin, NearestNeighbors) weights_ctype ) - self.handle.sync() + handle.sync() return out diff --git a/python/cuml/cuml/neighbors/kneighbors_regressor_mg.pyx b/python/cuml/cuml/neighbors/kneighbors_regressor_mg.pyx index e07efab869..297afabab6 100644 --- a/python/cuml/cuml/neighbors/kneighbors_regressor_mg.pyx +++ b/python/cuml/cuml/neighbors/kneighbors_regressor_mg.pyx @@ -4,7 +4,7 @@ # import typing -from cuml.internals import logger, reflect +from cuml.internals import get_handle, logger, reflect from cuml.internals.array import CumlArray from cuml.neighbors.nearest_neighbors_mg import NearestNeighborsMG @@ -110,7 +110,8 @@ class KNeighborsRegressorMG(NearestNeighborsMG): out_result_local_parts.push_back(new floatData_t( o_cai.ptr, n_rows * n_outputs)) - cdef handle_t* handle_ = self.handle.getHandle() + handle = get_handle(model=self) + cdef handle_t* handle_ = handle.getHandle() is_verbose = logger.should_log_for(logger.level_enum.debug) # Launch distributed operations @@ -131,7 +132,7 @@ class KNeighborsRegressorMG(NearestNeighborsMG): self.batch_size, is_verbose ) - self.handle.sync() + handle.sync() # Release memory type(self).free_mem(input) diff --git a/python/cuml/cuml/neighbors/nearest_neighbors.pyx b/python/cuml/cuml/neighbors/nearest_neighbors.pyx index f16024e75a..e32a6c0be5 100644 --- a/python/cuml/cuml/neighbors/nearest_neighbors.pyx +++ b/python/cuml/cuml/neighbors/nearest_neighbors.pyx @@ -16,7 +16,7 @@ from cuml.common.doc_utils import generate_docstring, insert_into_docstring from cuml.common.sparse_utils import is_dense, is_sparse from cuml.internals.array import CumlArray from cuml.internals.array_sparse import SparseCumlArray -from cuml.internals.base import Base +from cuml.internals.base import Base, get_handle from cuml.internals.input_utils import input_to_cuml_array from cuml.internals.interop import InteropMixin, UnsupportedOnGPU, to_gpu from cuml.internals.mixins import CMajorInputTagMixin, SparseInputTagMixin @@ -676,13 +676,14 @@ class NearestNeighbors(Base, # recreate them on load. with using_output_type("cuml"): X = getattr(self, "_fit_X", None) + handle = get_handle(model=self) if fit_method == "rbc": self._index = RBCIndex.build( - self.handle, X, self.effective_metric_, + handle, X, self.effective_metric_, ) else: self._index = ApproxIndex.build( - self.handle, + handle, X, self.effective_metric_, fit_method, @@ -742,7 +743,7 @@ class NearestNeighbors(Base, if self._fit_method in ('ivfflat', 'ivfpq'): self._index = ApproxIndex.build( - self.handle, + get_handle(model=self), self._fit_X, self.effective_metric_, self._fit_method, @@ -750,7 +751,9 @@ class NearestNeighbors(Base, p=self.p, ) elif self._fit_method == "rbc": - self._index = RBCIndex.build(self.handle, self._fit_X, self.effective_metric_) + self._index = RBCIndex.build( + get_handle(model=self), self._fit_X, self.effective_metric_ + ) return self @@ -873,8 +876,10 @@ class NearestNeighbors(Base, ) use_index = False + handle = get_handle(model=self) + if use_index: - return self._index.kneighbors(self.handle, X_m, n_neighbors) + return self._index.kneighbors(handle, X_m, n_neighbors) distances = CumlArray.zeros( (X_m.shape[0], n_neighbors), dtype=np.float32, order="C", index=X_m.index, @@ -883,7 +888,7 @@ class NearestNeighbors(Base, (X_m.shape[0], n_neighbors), dtype=np.int64, order="C", index=X_m.index, ) - cdef handle_t* handle_ = self.handle.getHandle() + cdef handle_t* handle_ = handle.getHandle() cdef vector[float*] inputs cdef vector[int] sizes inputs.push_back(self._fit_X.ptr) @@ -910,7 +915,7 @@ class NearestNeighbors(Base, distance_type, metric_arg ) - self.handle.sync() + handle.sync() if two_pass_precision: distances, indices = self._maybe_apply_two_pass_precision( @@ -990,7 +995,8 @@ class NearestNeighbors(Base, cdef int* indices_ptr = indices.ptr cdef float* distances_ptr = distances.ptr - cdef handle_t* handle_ = self.handle.getHandle() + handle = get_handle(model=self) + cdef handle_t* handle_ = handle.getHandle() with nogil: brute_force_knn( handle_[0], @@ -1014,7 +1020,7 @@ class NearestNeighbors(Base, metric, metric_arg, ) - self.handle.sync() + handle.sync() return distances, indices diff --git a/python/cuml/cuml/neighbors/nearest_neighbors_mg.pyx b/python/cuml/cuml/neighbors/nearest_neighbors_mg.pyx index 2275a94830..6bde435d58 100644 --- a/python/cuml/cuml/neighbors/nearest_neighbors_mg.pyx +++ b/python/cuml/cuml/neighbors/nearest_neighbors_mg.pyx @@ -6,7 +6,7 @@ import typing from cuml.common import input_to_cuml_array from cuml.common.opg_data_utils_mg import _build_part_inputs -from cuml.internals import logger, reflect +from cuml.internals import get_handle, logger, reflect from cuml.internals.array import CumlArray from cuml.neighbors import NearestNeighbors @@ -106,7 +106,8 @@ class NearestNeighborsMG(NearestNeighbors): # Build indices and distances outputs for native code interfacing result = type(self).alloc_local_output(local_query_rows, self.n_neighbors) - cdef handle_t* handle_ = self.handle.getHandle() + handle = get_handle(model=self) + cdef handle_t* handle_ = handle.getHandle() is_verbose = logger.should_log_for(logger.level_enum.debug) # Launch distributed operations @@ -126,7 +127,7 @@ class NearestNeighborsMG(NearestNeighbors): self.batch_size, is_verbose ) - self.handle.sync() + handle.sync() # Release memory type(self).free_mem(input, result) From e057d6177f6aac8c152b99cc794996880ef81703 Mon Sep 17 00:00:00 2001 From: Jim Crist-Harif Date: Thu, 18 Dec 2025 11:42:41 -0600 Subject: [PATCH 11/21] Use `get_handle` in `cuml.svm` Additional changes: - Adds `n_streams` parameter to `LinearSVC` since this estimator supports stream parallel operations. Defaulting to 1 to match prior behavior. - Deletes some long broken and bitrotted memleak tests --- python/cuml/cuml/svm/linear_svc.py | 13 +++- python/cuml/cuml/svm/linear_svr.py | 4 +- python/cuml/cuml/svm/svm_base.pyx | 14 ++-- python/cuml/tests/test_linear_svm.py | 14 ++-- python/cuml/tests/test_svm.py | 99 ---------------------------- 5 files changed, 26 insertions(+), 118 deletions(-) diff --git a/python/cuml/cuml/svm/linear_svc.py b/python/cuml/cuml/svm/linear_svc.py index 01f3fd34b2..a349e0bc88 100644 --- a/python/cuml/cuml/svm/linear_svc.py +++ b/python/cuml/cuml/svm/linear_svc.py @@ -14,7 +14,7 @@ from cuml.common.doc_utils import generate_docstring from cuml.common.exceptions import NotFittedError from cuml.internals.array import CumlArray -from cuml.internals.base import Base +from cuml.internals.base import Base, get_handle from cuml.internals.input_utils import input_to_cuml_array from cuml.internals.interop import ( InteropMixin, @@ -70,6 +70,8 @@ class LinearSVC(Base, InteropMixin, LinearClassifierMixin, ClassifierMixin): lbfgs_memory : int, default=5 Number of vectors approximating the hessian for the underlying QN solver (l-bfgs). + n_streams : int (default = 1) + Number of parallel streams used for fitting. multi_class : {'ovr'}, default='ovr' Multiclass classification strategy. Currently only 'ovr' is supported. handle : cuml.Handle @@ -149,6 +151,7 @@ def _get_param_names(cls): "max_iter", "linesearch_max_iter", "lbfgs_memory", + "n_streams", "multi_class", ] @@ -221,6 +224,7 @@ def __init__( max_iter=1000, linesearch_max_iter=100, lbfgs_memory=5, + n_streams=1, multi_class="ovr", handle=None, verbose=False, @@ -241,6 +245,7 @@ def __init__( self.max_iter = max_iter self.linesearch_max_iter = linesearch_max_iter self.lbfgs_memory = lbfgs_memory + self.n_streams = n_streams self.multi_class = multi_class @generate_docstring() @@ -267,7 +272,7 @@ def fit( ) coef, intercept, n_iter, prob_scale = cuml.svm.linear.fit( - self.handle, + get_handle(model=self, n_streams=self.n_streams), X, CumlArray(data=y.astype(X.dtype, copy=False)), sample_weight=sample_weight, @@ -349,7 +354,9 @@ def predict_proba(self, X, *, convert_dtype=True) -> CumlArray: order="C", ).array return cuml.svm.linear.compute_probabilities( - self.handle, scores, self.prob_scale_ + get_handle(model=self, n_streams=self.n_streams), + scores, + self.prob_scale_, ) @generate_docstring( diff --git a/python/cuml/cuml/svm/linear_svr.py b/python/cuml/cuml/svm/linear_svr.py index b89bfefd81..bf4fa16907 100644 --- a/python/cuml/cuml/svm/linear_svr.py +++ b/python/cuml/cuml/svm/linear_svr.py @@ -6,7 +6,7 @@ import cuml.svm.linear from cuml.common.array_descriptor import CumlArrayDescriptor from cuml.common.doc_utils import generate_docstring -from cuml.internals.base import Base +from cuml.internals.base import Base, get_handle from cuml.internals.input_utils import input_to_cuml_array from cuml.internals.interop import ( InteropMixin, @@ -240,7 +240,7 @@ def fit( ).array coef, intercept, n_iter, _ = cuml.svm.linear.fit( - self.handle, + get_handle(model=self), X, y, sample_weight=sample_weight, diff --git a/python/cuml/cuml/svm/svm_base.pyx b/python/cuml/cuml/svm/svm_base.pyx index 4d62c69d8d..2cbfb75c2a 100644 --- a/python/cuml/cuml/svm/svm_base.pyx +++ b/python/cuml/cuml/svm/svm_base.pyx @@ -12,7 +12,7 @@ import cuml.internals from cuml.common.array_descriptor import CumlArrayDescriptor from cuml.internals.array import CumlArray from cuml.internals.array_sparse import SparseCumlArray -from cuml.internals.base import Base +from cuml.internals.base import Base, get_handle from cuml.internals.interop import ( InteropMixin, UnsupportedOnGPU, @@ -422,7 +422,8 @@ class SVMBase(Base, else: param.max_iter = self.max_iter - cdef handle_t* handle_ = self.handle.getHandle() + handle = get_handle(model=self) + cdef handle_t* handle_ = handle.getHandle() cdef int n_rows, n_cols, X_nnz n_rows, n_cols = X.shape cdef uintptr_t X_ptr, X_data_ptr, y_ptr, sample_weight_ptr @@ -440,7 +441,7 @@ class SVMBase(Base, sample_weight_ptr = 0 if sample_weight is None else sample_weight.ptr cdef int n_iter - cdef _SVMModel internal = _SVMModel.new(self.handle, is_float32) + cdef _SVMModel internal = _SVMModel.new(handle, is_float32) with nogil: if is_sparse: @@ -499,7 +500,7 @@ class SVMBase(Base, internal.model_d[0], sample_weight_ptr, ) - self.handle.sync() + handle.sync() support, support_vectors, dual_coef, intercept = internal.unpack() @@ -595,7 +596,8 @@ class SVMBase(Base, cdef uintptr_t out_ptr = out.ptr cdef double cache_size = self.cache_size - cdef handle_t* handle_ = self.handle.getHandle() + handle = get_handle(model=self) + cdef handle_t* handle_ = handle.getHandle() # Call predict with nogil: @@ -655,7 +657,7 @@ class SVMBase(Base, cache_size, False, ) - self.handle.sync() + handle.sync() return out diff --git a/python/cuml/tests/test_linear_svm.py b/python/cuml/tests/test_linear_svm.py index 98a9d6fafd..ccc743fb22 100644 --- a/python/cuml/tests/test_linear_svm.py +++ b/python/cuml/tests/test_linear_svm.py @@ -6,7 +6,6 @@ import numpy as np import pytest import sklearn.svm -from pylibraft.common import Handle from sklearn.datasets import make_classification, make_regression from sklearn.model_selection import train_test_split @@ -289,14 +288,13 @@ def test_linear_svr_n_iter_max_iter(loss): @pytest.mark.parametrize("penalty", ["l1", "l2"]) -@pytest.mark.parametrize("parallel_streams", [False, True]) -def test_linear_svc_n_iter_max_iter(penalty, parallel_streams): - handle = Handle(n_streams=4) if parallel_streams else None +@pytest.mark.parametrize("n_streams", [1, 4]) +def test_linear_svc_n_iter_max_iter(penalty, n_streams): X, y = make_classification(random_state=42) - model = cuml.LinearSVC(penalty=penalty, handle=handle).fit(X, y) + model = cuml.LinearSVC(penalty=penalty, n_streams=n_streams).fit(X, y) assert model.n_iter_ <= model.max_iter - model = cuml.LinearSVC(penalty=penalty, handle=handle, max_iter=10).fit( - X, y - ) + model = cuml.LinearSVC( + penalty=penalty, n_streams=n_streams, max_iter=10 + ).fit(X, y) assert model.n_iter_ == 10 diff --git a/python/cuml/tests/test_svm.py b/python/cuml/tests/test_svm.py index fde0a51d53..5af17e3098 100644 --- a/python/cuml/tests/test_svm.py +++ b/python/cuml/tests/test_svm.py @@ -451,105 +451,6 @@ def get_memsize(svc): return ms -@pytest.mark.xfail(reason="Need rapidsai/rmm#415 to detect memleak robustly") -@pytest.mark.memleak -@pytest.mark.parametrize("params", [{"kernel": "rbf", "C": 1, "gamma": 1}]) -@pytest.mark.parametrize( - "n_rows", [unit_param(500), quality_param(1000), stress_param(1000)] -) -@pytest.mark.parametrize( - "n_iter", [unit_param(10), quality_param(100), stress_param(1000)] -) -@pytest.mark.parametrize("n_cols", [1000]) -def test_svm_memleak(params, n_rows, n_iter, n_cols, dataset="blobs"): - """ - Test whether there is any memory leak. - - .. note:: small `n_rows`, and `n_cols` values will result in small model - size, that will not be measured by get_memory_info. - - """ - X_train, X_test, y_train, y_test = make_dataset(dataset, n_rows, n_cols) - handle = cuml.Handle() - # Warmup. Some modules that are used in SVC allocate space on the device - # and consume memory. Here we make sure that this allocation is done - # before the first call to get_memory_info. - tmp = cu_svm.SVC(handle=handle, **params) - tmp.fit(X_train, y_train) - ms = get_memsize(tmp) - print( - "Memory consumption of SVC object is {} MiB".format( - ms / (1024 * 1024.0) - ) - ) - - free_mem = cuda.current_context().get_memory_info()[0] - - # Check first whether the get_memory_info gives us the correct memory - # footprint - cuSVC = cu_svm.SVC(handle=handle, **params) - cuSVC.fit(X_train, y_train) - delta_mem = free_mem - cuda.current_context().get_memory_info()[0] - assert delta_mem >= ms - - # Main test loop - b_sum = 0 - for i in range(n_iter): - cuSVC = cu_svm.SVC(handle=handle, **params) - cuSVC.fit(X_train, y_train) - b_sum += cuSVC.intercept_ - cuSVC.predict(X_train) - - del cuSVC - handle.sync() - delta_mem = free_mem - cuda.current_context().get_memory_info()[0] - print("Delta GPU mem: {} bytes".format(delta_mem)) - assert delta_mem == 0 - - -@pytest.mark.xfail(reason="Need rapidsai/rmm#415 to detect memleak robustly") -@pytest.mark.memleak -@pytest.mark.parametrize( - "params", [{"kernel": "poly", "degree": 30, "C": 1, "gamma": 1}] -) -def test_svm_memleak_on_exception( - params, n_rows=1000, n_iter=10, n_cols=1000, dataset="blobs" -): - """ - Test whether there is any mem leak when we exit training with an exception. - The poly kernel with degree=30 will overflow, and triggers the - 'SMO error: NaN found...' exception. - """ - X_train, y_train = make_blobs( - n_samples=n_rows, n_features=n_cols, random_state=137, centers=2 - ) - X_train = X_train.astype(np.float32) - handle = cuml.Handle() - - # Warmup. Some modules that are used in SVC allocate space on the device - # and consume memory. Here we make sure that this allocation is done - # before the first call to get_memory_info. - tmp = cu_svm.SVC(handle=handle, **params) - with pytest.raises(RuntimeError): - tmp.fit(X_train, y_train) - # SMO error: NaN found during fitting. - - free_mem = cuda.current_context().get_memory_info()[0] - - # Main test loop - for i in range(n_iter): - cuSVC = cu_svm.SVC(handle=handle, **params) - with pytest.raises(RuntimeError): - cuSVC.fit(X_train, y_train) - # SMO error: NaN found during fitting. - - del cuSVC - handle.sync() - delta_mem = free_mem - cuda.current_context().get_memory_info()[0] - print("Delta GPU mem: {} bytes".format(delta_mem)) - assert delta_mem == 0 - - def make_regression_dataset(dataset, n_rows, n_cols): np.random.seed(137) if dataset == "reg1": From ef155caa929be8f84de9908c674ebc48b7b00693 Mon Sep 17 00:00:00 2001 From: Jim Crist-Harif Date: Thu, 18 Dec 2025 11:56:42 -0600 Subject: [PATCH 12/21] Use `get_handle` in `cuml.tsa` --- python/cuml/cuml/tsa/arima.pyx | 32 ++++++++++++++++++--------- python/cuml/cuml/tsa/auto_arima.pyx | 14 ++++++------ python/cuml/cuml/tsa/holtwinters.pyx | 12 +++++----- python/cuml/cuml/tsa/seasonality.py | 8 +++---- python/cuml/cuml/tsa/stationarity.pyx | 6 ++--- 5 files changed, 41 insertions(+), 31 deletions(-) diff --git a/python/cuml/cuml/tsa/arima.pyx b/python/cuml/cuml/tsa/arima.pyx index d9011233c2..87c69aa8c3 100644 --- a/python/cuml/cuml/tsa/arima.pyx +++ b/python/cuml/cuml/tsa/arima.pyx @@ -9,7 +9,7 @@ import numpy as np from cuml.common.array_descriptor import CumlArrayDescriptor from cuml.internals import logger, nvtx, reflect, run_in_internal_context from cuml.internals.array import CumlArray -from cuml.internals.base import Base +from cuml.internals.base import Base, get_handle from cuml.internals.input_utils import input_to_cuml_array from cuml.tsa.batched_lbfgs import batched_fmin_lbfgs_b @@ -390,7 +390,8 @@ class ARIMA(Base): cdef uintptr_t d_y_diff_ptr = self._d_y_diff.ptr cdef uintptr_t d_exog_ptr cdef uintptr_t d_exog_diff_ptr - cdef handle_t* handle_ = self.handle.getHandle() + handle = get_handle(model=self) + cdef handle_t* handle_ = handle.getHandle() cdef ARIMAOrder cpp_order_diff = self.order # Detect missing observations @@ -436,7 +437,8 @@ class ARIMA(Base): def _ic(self, ic_type: str): """Wrapper around C++ information_criterion """ - cdef handle_t* handle_ = self.handle.getHandle() + handle = get_handle(model=self) + cdef handle_t* handle_ = handle.getHandle() cdef ARIMAOrder order = self.order cdef ARIMAOrder order_kf = \ @@ -663,7 +665,8 @@ class ARIMA(Base): raise ValueError("A value was given for `exog` but only in-sample" " predictions were requested") - cdef handle_t* handle_ = self.handle.getHandle() + handle = get_handle(model=self) + cdef handle_t* handle_ = handle.getHandle() predict_size = end - start # Future values of the exogenous variables @@ -813,7 +816,8 @@ class ARIMA(Base): cdef uintptr_t d_exog_ptr = NULL if order.n_exog: d_exog_ptr = self.d_exog.ptr - cdef handle_t* handle_ = self.handle.getHandle() + handle = get_handle(model=self) + cdef handle_t* handle_ = handle.getHandle() # Call C++ function estimate_x0(handle_[0], cpp_params, d_y_ptr, @@ -969,7 +973,8 @@ class ARIMA(Base): if order.n_exog: d_exog_kf_ptr = self._d_exog_diff.ptr if diff else self.d_exog.ptr - cdef handle_t* handle_ = self.handle.getHandle() + handle = get_handle(model=self) + cdef handle_t* handle_ = handle.getHandle() n_obs_kf = (self.n_obs_diff if diff else self.n_obs) @@ -1046,7 +1051,8 @@ class ARIMA(Base): if order.n_exog: d_exog_kf_ptr = self._d_exog_diff.ptr if diff else self.d_exog.ptr - cdef handle_t* handle_ = self.handle.getHandle() + handle = get_handle(model=self) + cdef handle_t* handle_ = handle.getHandle() cdef uintptr_t d_temp_mem = self._temp_mem.ptr arima_mem_ptr = new ARIMAMemory[double]( @@ -1074,7 +1080,8 @@ class ARIMA(Base): # as it uses the device parameter arrays and not a host vector. # Also, it always uses the MLE method, trans=False and truncate=0 - cdef handle_t* handle_ = self.handle.getHandle() + handle = get_handle(model=self) + cdef handle_t* handle_ = handle.getHandle() cdef vector[double] vec_loglike vec_loglike.resize(self.batch_size) @@ -1124,7 +1131,8 @@ class ARIMA(Base): Packed parameter array, grouped by series. Shape: (n_params * batch_size,) """ - cdef handle_t* handle_ = self.handle.getHandle() + handle = get_handle(model=self) + cdef handle_t* handle_ = handle.getHandle() self._create_arrays() @@ -1153,7 +1161,8 @@ class ARIMA(Base): Packed parameter array, grouped by series. Shape: (n_params * batch_size,) """ - cdef handle_t* handle_ = self.handle.getHandle() + handle = get_handle(model=self) + cdef handle_t* handle_ = handle.getHandle() cdef ARIMAOrder order = self.order cdef ARIMAParams[double] cpp_params = ARIMAParamsWrapper(self).params @@ -1187,7 +1196,8 @@ class ARIMA(Base): cdef ARIMAOrder order = self.order N = self.complexity - cdef handle_t* handle_ = self.handle.getHandle() + handle = get_handle(model=self) + cdef handle_t* handle_ = handle.getHandle() Tx = np.zeros(self.batch_size * N) cdef uintptr_t d_temp_mem = self._temp_mem.ptr diff --git a/python/cuml/cuml/tsa/auto_arima.pyx b/python/cuml/cuml/tsa/auto_arima.pyx index 3adc247673..58c513fa06 100644 --- a/python/cuml/cuml/tsa/auto_arima.pyx +++ b/python/cuml/cuml/tsa/auto_arima.pyx @@ -7,13 +7,12 @@ import typing import cupy as cp import numpy as np -from pylibraft.common.handle import Handle from cuml.common import input_to_cuml_array, using_output_type from cuml.common.array_descriptor import CumlArrayDescriptor from cuml.internals import logger, reflect, run_in_internal_context from cuml.internals.array import CumlArray -from cuml.internals.base import Base +from cuml.internals.base import Base, get_handle from cuml.tsa.arima import ARIMA from cuml.tsa.seasonality import seas_test from cuml.tsa.stationarity import kpss_test @@ -191,7 +190,8 @@ class AutoARIMA(Base): @run_in_internal_context def _initial_calc(self): cdef uintptr_t d_y_ptr = self.d_y.ptr - cdef handle_t* handle_ = self.handle.getHandle() + handle = get_handle(model=self) + cdef handle_t* handle_ = handle.getHandle() # Detect missing observations missing = detect_missing(handle_[0], d_y_ptr, @@ -603,7 +603,7 @@ def _divide_by_mask(original, mask, batch_id, handle=None): batch_size = original.shape[1] if len(original.shape) > 1 else 1 if handle is None: - handle = Handle() + handle = get_handle() cdef handle_t* handle_ = handle.getHandle() index = CumlArray.empty(batch_size, np.int32) @@ -723,7 +723,7 @@ def _divide_by_min(original, metrics, batch_id, handle=None): batch_size = original.shape[1] if len(original.shape) > 1 else 1 if handle is None: - handle = Handle() + handle = get_handle() cdef handle_t* handle_ = handle.getHandle() batch_buffer = CumlArray.empty(batch_size, np.int32) @@ -830,7 +830,7 @@ def _build_division_map(id_tracker, batch_size, handle=None): Position of each member in the respective sub-batch """ if handle is None: - handle = Handle() + handle = get_handle() cdef handle_t* handle_ = handle.getHandle() n_sub = len(id_tracker) @@ -888,7 +888,7 @@ def _merge_series(data_in, id_to_sub, id_to_pos, batch_size, handle=None): n_sub = len(data_in) if handle is None: - handle = Handle() + handle = get_handle() cdef handle_t* handle_ = handle.getHandle() cdef vector[uintptr_t] in_ptr diff --git a/python/cuml/cuml/tsa/holtwinters.pyx b/python/cuml/cuml/tsa/holtwinters.pyx index 6a2666536b..819842f534 100644 --- a/python/cuml/cuml/tsa/holtwinters.pyx +++ b/python/cuml/cuml/tsa/holtwinters.pyx @@ -8,7 +8,7 @@ import numpy as np from cuml.common import using_output_type from cuml.common.array_descriptor import CumlArrayDescriptor from cuml.internals.array import CumlArray -from cuml.internals.base import Base +from cuml.internals.base import Base, get_handle from cuml.internals.input_utils import input_to_cupy_array from cuml.internals.outputs import run_in_internal_context @@ -298,7 +298,8 @@ class ExponentialSmoothing(Base): &season_coef_offset, &error_len) - cdef handle_t* handle_ = self.handle.getHandle() + handle = get_handle(model=self) + cdef handle_t* handle_ = handle.getHandle() cdef uintptr_t level_ptr, trend_ptr, season_ptr, SSE_ptr self.level = CumlArray.zeros(components_len, dtype=self.dtype) @@ -343,7 +344,7 @@ class ExponentialSmoothing(Base): self.season = self.season.reshape((self.ts_num, num_rows), order='F') - self.handle.sync() + handle.sync() self.fit_executed_flag = True return self @@ -369,7 +370,8 @@ class ExponentialSmoothing(Base): """ cdef uintptr_t forecast_ptr, level_ptr, trend_ptr, season_ptr - cdef handle_t* handle_ = self.handle.getHandle() + handle = get_handle(model=self) + cdef handle_t* handle_ = handle.getHandle() if not isinstance(h, int) or (not isinstance(index, int) and index is not None): raise TypeError("Input arguments must be of type int." @@ -414,7 +416,7 @@ class ExponentialSmoothing(Base): self.forecasted_points =\ self.forecasted_points.reshape((self.ts_num, h), order='F') - self.handle.sync() + handle.sync() if index is None: if self.ts_num == 1: diff --git a/python/cuml/cuml/tsa/seasonality.py b/python/cuml/cuml/tsa/seasonality.py index f48023e1b8..6215e488c6 100644 --- a/python/cuml/cuml/tsa/seasonality.py +++ b/python/cuml/cuml/tsa/seasonality.py @@ -2,11 +2,9 @@ # SPDX-License-Identifier: Apache-2.0 import numpy as np +from cuml.internals import get_handle, reflect from cuml.internals.array import CumlArray from cuml.internals.input_utils import input_to_cuml_array, input_to_host_array -from cuml.internals.outputs import reflect - -# TODO: #2234 and #2235 def python_seas_test(y, batch_size, n_obs, s, threshold=0.64): @@ -60,6 +58,9 @@ def seas_test(y, s, handle=None, convert_dtype=True) -> CumlArray: s ) ) + # `handle` is fully unused in this function - calling `get_handle` here just + # to raise the uniform deprecation warning + get_handle(handle=handle) # At the moment we use a host array h_y, n_obs, batch_size, _ = input_to_host_array( @@ -68,7 +69,6 @@ def seas_test(y, s, handle=None, convert_dtype=True) -> CumlArray: check_dtype=[np.float32, np.float64], ) - # Temporary: Python implementation python_res = python_seas_test(h_y, batch_size, n_obs, s) d_res, *_ = input_to_cuml_array( np.array(python_res), diff --git a/python/cuml/cuml/tsa/stationarity.pyx b/python/cuml/cuml/tsa/stationarity.pyx index e578dd8198..2213b620eb 100644 --- a/python/cuml/cuml/tsa/stationarity.pyx +++ b/python/cuml/cuml/tsa/stationarity.pyx @@ -1,11 +1,10 @@ # SPDX-FileCopyrightText: Copyright (c) 2019-2025, NVIDIA CORPORATION. # SPDX-License-Identifier: Apache-2.0 import numpy as np -from pylibraft.common.handle import Handle +from cuml.internals import get_handle, reflect from cuml.internals.array import CumlArray from cuml.internals.input_utils import input_to_cuml_array -from cuml.internals.outputs import reflect from libc.stdint cimport uintptr_t from libcpp cimport bool as boolcpp @@ -73,8 +72,7 @@ def kpss_test(y, d=0, D=0, s=0, pval_threshold=0.05, check_dtype=[np.float32, np.float64]) cdef uintptr_t d_y_ptr = d_y.ptr - if handle is None: - handle = Handle() + handle = get_handle(handle=handle) cdef handle_t* handle_ = handle.getHandle() results = CumlArray.empty(batch_size, dtype=bool) From 3bca25e11e093b9bc3ba218168385a2843a02769 Mon Sep 17 00:00:00 2001 From: Jim Crist-Harif Date: Thu, 18 Dec 2025 12:12:45 -0600 Subject: [PATCH 13/21] Use `get_handle` in `cuml.explainer` --- python/cuml/cuml/explainer/base.pyx | 15 ++++----- python/cuml/cuml/explainer/common.py | 27 ---------------- python/cuml/cuml/explainer/kernel_shap.pyx | 7 ++-- .../cuml/cuml/explainer/permutation_shap.pyx | 9 +++--- .../tests/explainer/test_explainer_base.py | 32 ++----------------- .../tests/explainer/test_explainer_common.py | 22 ------------- 6 files changed, 17 insertions(+), 95 deletions(-) diff --git a/python/cuml/cuml/explainer/base.pyx b/python/cuml/cuml/explainer/base.pyx index 37951227d3..4c726e9a29 100644 --- a/python/cuml/cuml/explainer/base.pyx +++ b/python/cuml/cuml/explainer/base.pyx @@ -10,12 +10,12 @@ import pandas as pd import cuml.internals.logger as logger from cuml.explainer.common import ( get_cai_ptr, - get_handle_from_cuml_model_func, get_link_fn_from_str_or_fn, get_tag_from_model_func, model_func_call, output_list_shap_values, ) +from cuml.internals.base import DeprecatedHandleDescriptor, get_handle from cuml.internals.input_utils import input_to_cupy_array, input_to_host_array from libc.stdint cimport uintptr_t @@ -81,6 +81,7 @@ class SHAPBase(): For compatibility with SHAP's graphing libraries, specify `numpy`. """ + handle = DeprecatedHandleDescriptor() def __init__(self, *, @@ -108,11 +109,7 @@ class SHAPBase(): else: self.time_performance = False - if handle is None: - self.handle = get_handle_from_cuml_model_func(model, - create_new=True) - else: - self.handle = handle + self.handle = handle if order is None: self.order = get_tag_from_model_func(func=model, @@ -341,8 +338,8 @@ class SHAPBase(): order=self.masker.order ) - cdef handle_t* handle_ = \ - self.handle.getHandle() + handle = get_handle(model=self) + cdef handle_t* handle_ = handle.getHandle() cdef uintptr_t row_ptr, bg_ptr, idx_ptr, masked_ptr masked_ptr = get_cai_ptr(masked_inputs) @@ -387,7 +384,7 @@ class SHAPBase(): # Cast result back to float64 masked_inputs[:] = masked_inputs_f32.astype(cp.float64) - self.handle.sync() + handle.sync() main_effects = model_func_call(masked_inputs) - self._expected_value return main_effects diff --git a/python/cuml/cuml/explainer/common.py b/python/cuml/cuml/explainer/common.py index 299c3dffcb..7e69ece891 100644 --- a/python/cuml/cuml/explainer/common.py +++ b/python/cuml/cuml/explainer/common.py @@ -2,11 +2,8 @@ # SPDX-FileCopyrightText: Copyright (c) 2020-2025, NVIDIA CORPORATION. # SPDX-License-Identifier: Apache-2.0 # - import cupy as cp -from pylibraft.common.handle import Handle -from cuml.internals.base import Base from cuml.internals.input_utils import input_to_cupy_array @@ -35,30 +32,6 @@ def get_tag_from_model_func(func, tag, default=None): return default -def get_handle_from_cuml_model_func(func, create_new=False): - """ - Function to obtain a RAFT handle from the object that `func` is bound to - if possible. - - Parameters - ---------- - func: object - Function to check whether the object it is bound to has a _get_tags - attribute, and return tags from it. - create_new: boolean (default = False) - Whether to return a new RAFT handle if none could be fetched. Otherwise - the function will return None. - """ - owner = getattr(func, "__self__", None) - - if owner is not None and isinstance(owner, Base): - if owner.handle is not None: - return owner.handle - - handle = Handle() if create_new else None - return handle - - def model_func_call(X, model_func, gpu_model=False): """ Function to call `model_func(X)` using either `NumPy` arrays if diff --git a/python/cuml/cuml/explainer/kernel_shap.pyx b/python/cuml/cuml/explainer/kernel_shap.pyx index 16ed6f4538..b49d5ef0ef 100644 --- a/python/cuml/cuml/explainer/kernel_shap.pyx +++ b/python/cuml/cuml/explainer/kernel_shap.pyx @@ -12,6 +12,7 @@ import numpy as np from cuml.explainer.base import SHAPBase from cuml.explainer.common import get_cai_ptr, model_func_call +from cuml.internals import get_handle from cuml.internals.input_utils import input_to_cupy_array from cuml.linear_model import Lasso, LinearRegression @@ -290,8 +291,8 @@ class KernelExplainer(SHAPBase): row, _, _, _ = \ input_to_cupy_array(row, order=self.order) - cdef handle_t* handle_ = \ - self.handle.getHandle() + handle = get_handle(model=self) + cdef handle_t* handle_ = handle.getHandle() cdef uintptr_t row_ptr, bg_ptr, ds_ptr, x_ptr, smp_ptr row_ptr = get_cai_ptr(row) @@ -353,7 +354,7 @@ class KernelExplainer(SHAPBase): maxsample, self.random_state) - self.handle.sync() + handle.sync() model_timer = time.time() # evaluate model on combinations diff --git a/python/cuml/cuml/explainer/permutation_shap.pyx b/python/cuml/cuml/explainer/permutation_shap.pyx index ec9f0d3f57..072b087821 100644 --- a/python/cuml/cuml/explainer/permutation_shap.pyx +++ b/python/cuml/cuml/explainer/permutation_shap.pyx @@ -9,6 +9,7 @@ import numpy as np from cuml.explainer.base import SHAPBase from cuml.explainer.common import get_cai_ptr, model_func_call +from cuml.internals import get_handle from libc.stdint cimport uintptr_t from libcpp cimport bool @@ -232,8 +233,8 @@ class PermutationExplainer(SHAPBase): total_timer = time.time() inds = cp.arange(self.ncols, dtype=cp.int32) - cdef handle_t* handle_ = \ - self.handle.getHandle() + handle = get_handle(model=self) + cdef handle_t* handle_ = handle.getHandle() cdef uintptr_t row_ptr, bg_ptr, idx_ptr, ds_ptr, shap_ptr, y_hat_ptr cdef uintptr_t ds_ptr_f32 cdef uintptr_t bg_ptr_f32 @@ -286,7 +287,7 @@ class PermutationExplainer(SHAPBase): # Cast result back to float64 self._synth_data[:] = synth_data_f32.astype(cp.float64) - self.handle.sync() + handle.sync() # evaluate model on combinations model_timer = time.time() @@ -338,7 +339,7 @@ class PermutationExplainer(SHAPBase): # Cast result back to float64 shap_values[i][idx] = shap_values_f32.astype(cp.float64) - self.handle.sync() + handle.sync() for i in range(self.model_dimensions): shap_values[i][idx] = shap_values[i][idx] / (2 * npermutations) diff --git a/python/cuml/tests/explainer/test_explainer_base.py b/python/cuml/tests/explainer/test_explainer_base.py index c9fb2a454d..b037869010 100644 --- a/python/cuml/tests/explainer/test_explainer_base.py +++ b/python/cuml/tests/explainer/test_explainer_base.py @@ -2,32 +2,24 @@ # SPDX-FileCopyrightText: Copyright (c) 2020-2025, NVIDIA CORPORATION. # SPDX-License-Identifier: Apache-2.0 # - import cudf import cupy as cp import numpy as np import pytest -from pylibraft.common.handle import Handle from cuml import LinearRegression as cuLR from cuml.explainer.base import SHAPBase -@pytest.mark.parametrize("handle", [True, False]) @pytest.mark.parametrize("dtype", [np.float32, np.float64, None]) @pytest.mark.parametrize("order", ["C", None]) -def test_init_explainer_base_init_cuml_model(handle, dtype, order): +def test_init_explainer_base_init_cuml_model(dtype, order): bg = np.arange(10).reshape(5, 2).astype(np.float32) y = np.arange(5).astype(np.float32) bg_df = cudf.DataFrame(bg) model = cuLR().fit(bg, y) - if handle: - handle = Handle() - else: - handle = None - explainer = SHAPBase( model=model.predict, background=bg_df, @@ -36,7 +28,6 @@ def test_init_explainer_base_init_cuml_model(handle, dtype, order): verbose=2, random_state=None, is_gpu_model=None, - handle=handle, dtype=None, output_type=None, ) @@ -54,28 +45,16 @@ def test_init_explainer_base_init_cuml_model(handle, dtype, order): else: assert explainer.order == order - # check that we keep the model's handle if one is not passed explicitly - if handle is not None: - assert explainer.handle == handle - else: - assert explainer.handle == model.handle - -@pytest.mark.parametrize("handle", [True, False]) @pytest.mark.parametrize("dtype", [np.float32, np.float64, None]) @pytest.mark.parametrize("order", ["C", None]) @pytest.mark.parametrize("is_gpu_model", [True, False, None]) @pytest.mark.parametrize("output_type", ["cupy", None]) def test_init_explainer_base_init_abritrary_model( - handle, dtype, order, is_gpu_model, output_type + dtype, order, is_gpu_model, output_type ): bg = np.arange(10).reshape(5, 2).astype(np.float32) - if handle: - handle = Handle() - else: - handle = None - explainer = SHAPBase( model=dummy_func, background=bg, @@ -85,7 +64,6 @@ def test_init_explainer_base_init_abritrary_model( verbose=2, random_state=None, is_gpu_model=is_gpu_model, - handle=handle, dtype=None, output_type=output_type, ) @@ -110,12 +88,6 @@ def test_init_explainer_base_init_abritrary_model( else: assert explainer.order == order - # check that we keep the model's handle if one is not passed explicitly - if handle is not None: - assert explainer.handle == handle - else: - isinstance(explainer.handle, Handle) - def test_init_explainer_base_wrong_dtype(): with pytest.raises(ValueError): diff --git a/python/cuml/tests/explainer/test_explainer_common.py b/python/cuml/tests/explainer/test_explainer_common.py index f70e6ce5ee..daf8eca57c 100644 --- a/python/cuml/tests/explainer/test_explainer_common.py +++ b/python/cuml/tests/explainer/test_explainer_common.py @@ -6,7 +6,6 @@ import cupy as cp import numpy as np import pytest -from pylibraft.common.handle import Handle from sklearn.linear_model import LinearRegression as skreg import cuml @@ -15,7 +14,6 @@ from cuml.datasets import make_regression from cuml.explainer.common import ( get_cai_ptr, - get_handle_from_cuml_model_func, get_link_fn_from_str_or_fn, get_tag_from_model_func, link_dict, @@ -101,26 +99,6 @@ def test_get_tag_from_model_func(model): assert res != "FFF" -@pytest.mark.parametrize("model", list(models.values())) -def test_get_handle_from_cuml_model_func(model): - mod = create_dummy_model(model) - - handle = get_handle_from_cuml_model_func( - mod._get_param_names, create_new=True - ) - - assert isinstance(handle, Handle) - - -@pytest.mark.parametrize("create_new", [True, False]) -def test_get_handle_from_dummy_func(create_new): - handle = get_handle_from_cuml_model_func(dummy_func, create_new=create_new) - - res = isinstance(handle, Handle) - - assert res == create_new - - def test_model_func_call_gpu(): X, y = make_regression( n_samples=81, From 8878fa5842883fb2c7bf77fcc253dcaecd964c7b Mon Sep 17 00:00:00 2001 From: Jim Crist-Harif Date: Thu, 18 Dec 2025 13:37:59 -0600 Subject: [PATCH 14/21] Add tests for deprecation warnings --- .../tests/explainer/test_explainer_base.py | 17 +++ python/cuml/tests/test_base.py | 107 ++++++++++++++---- 2 files changed, 102 insertions(+), 22 deletions(-) diff --git a/python/cuml/tests/explainer/test_explainer_base.py b/python/cuml/tests/explainer/test_explainer_base.py index b037869010..b807e1c870 100644 --- a/python/cuml/tests/explainer/test_explainer_base.py +++ b/python/cuml/tests/explainer/test_explainer_base.py @@ -5,12 +5,29 @@ import cudf import cupy as cp import numpy as np +import pylibraft.common.handle import pytest from cuml import LinearRegression as cuLR from cuml.explainer.base import SHAPBase +def test_handle_deprecated(): + bg = np.arange(10).reshape(5, 2).astype(np.float32) + y = np.arange(5).astype(np.float32) + bg_df = cudf.DataFrame(bg) + model = cuLR().fit(bg, y) + + handle = pylibraft.common.handle.Handle() + + with pytest.warns(FutureWarning, match="handle"): + explainer = SHAPBase( + model=model.predict, background=bg_df, handle=handle + ) + + assert explainer.handle is handle + + @pytest.mark.parametrize("dtype", [np.float32, np.float64, None]) @pytest.mark.parametrize("order", ["C", None]) def test_init_explainer_base_init_cuml_model(dtype, order): diff --git a/python/cuml/tests/test_base.py b/python/cuml/tests/test_base.py index f13f84d03b..50df323b9c 100644 --- a/python/cuml/tests/test_base.py +++ b/python/cuml/tests/test_base.py @@ -2,12 +2,13 @@ # SPDX-License-Identifier: Apache-2.0 # import inspect +import warnings import numpy as np import numpydoc.docscrape import pandas as pd +import pylibraft.common.handle import pytest -from pylibraft.common.cuda import Stream from sklearn.datasets import ( make_classification, make_multilabel_classification, @@ -18,33 +19,13 @@ from cuml._thirdparty.sklearn.utils.skl_dependencies import ( BaseEstimator as sklBaseEstimator, ) +from cuml.internals import get_handle from cuml.testing.datasets import small_classification_dataset from cuml.testing.utils import get_all_base_subclasses all_base_children = get_all_base_subclasses() -def test_base_class_usage(): - # Ensure base class returns the 3 main properties needed by all classes - base = cuml.Base() - base.handle.sync() - base_params = base._get_param_names() - - assert "handle" in base_params - assert "verbose" in base_params - assert "output_type" in base_params - - del base - - -def test_base_class_usage_with_handle(): - stream = Stream() - handle = cuml.Handle(stream=stream) - base = cuml.Base(handle=handle) - base.handle.sync() - del base - - @pytest.mark.parametrize("datatype", ["float32", "float64"]) @pytest.mark.parametrize("use_integer_n_features", [True, False]) def test_base_n_features_in(datatype, use_integer_n_features): @@ -301,6 +282,88 @@ def test_common_signatures(cls, method): assert param.name not in {"X", "y", "sample_weight"} +@pytest.mark.parametrize( + "cls", + [ + cls + for cls in all_base_children.values() + if not ( + "Base" in cls.__name__ + or cls.__module__.startswith("cuml.tsa") + or cls.__name__ in ["ColumnTransformer"] + ) + ], +) +def test_handle_deprecated(cls): + if cls.__name__ in ("OneVsRestClassifier", "OneVsOneClassifier"): + + def create(handle=None): + return cls(cuml.SVC(), handle=handle) + else: + create = cls + + with warnings.catch_warnings(): + warnings.simplefilter("error") + # No warning by default + model = create() + assert get_handle(model=model) is get_handle() + + handle = pylibraft.common.handle.Handle() + with pytest.warns(FutureWarning, match="handle") as rec: + model = create(handle=handle) + + assert get_handle(model=model) is handle + + if cls in (cuml.UMAP, cuml.HDBSCAN): + assert "device_ids" in str(rec[0].message) + elif cls in ( + cuml.LinearSVC, + cuml.RandomForestClassifier, + cuml.RandomForestRegressor, + ): + assert "n_streams" in str(rec[0].message) + + +def test_get_handle(): + # Threadlocal is cached + assert get_handle() is get_handle() + + # Handle arg warns and returns + handle = pylibraft.common.handle.Handle() + with pytest.warns(FutureWarning, match="handle"): + res = get_handle(handle=handle) + assert res is handle + + # Handle arg takes precedence + with pytest.warns(FutureWarning, match="handle"): + res = get_handle(handle=handle, n_streams=4) + assert res is handle + + # n_streams doesn't use the threadlocal handle + res = get_handle(n_streams=4) + assert res is not get_handle() + assert isinstance(res, pylibraft.common.handle.Handle) + + +def test_get_handle_device_ids(): + for device_ids in ["all", [0]]: + res = get_handle(device_ids=device_ids) + assert isinstance(res, pylibraft.common.handle.DeviceResourcesSNMG) + + # None uses default handle + assert get_handle(device_ids=None) is get_handle() + + # Handle arg takes precedence + handle = pylibraft.common.handle.Handle() + with pytest.warns(FutureWarning, match="handle"): + res = get_handle(handle=handle, device_ids="all") + assert res is handle + + with pytest.raises(ValueError, match="n_streams"): + # Can't mix n_streams and device_ids + get_handle(n_streams=4, device_ids="all") + + @pytest.mark.parametrize( "cls", [ From 354363261a41790839c64fcdb3ec090b1911489e Mon Sep 17 00:00:00 2001 From: Jim Crist-Harif Date: Thu, 18 Dec 2025 14:21:06 -0600 Subject: [PATCH 15/21] Update docstrings --- python/cuml/cuml/cluster/agglomerative.pyx | 14 ++-- python/cuml/cuml/cluster/dbscan.pyx | 14 ++-- python/cuml/cuml/cluster/hdbscan/hdbscan.pyx | 14 ++-- python/cuml/cuml/cluster/kmeans.pyx | 14 ++-- .../cuml/cuml/cluster/spectral_clustering.pyx | 27 ++++--- python/cuml/cuml/dask/cluster/kmeans.py | 13 ++-- python/cuml/cuml/dask/decomposition/pca.py | 14 ++-- python/cuml/cuml/dask/decomposition/tsvd.py | 14 ++-- .../dask/ensemble/randomforestclassifier.py | 14 ++-- .../dask/ensemble/randomforestregressor.py | 14 ++-- .../cuml/dask/linear_model/elastic_net.py | 14 ++-- .../dask/neighbors/kneighbors_classifier.py | 14 ++-- .../dask/neighbors/kneighbors_regressor.py | 14 ++-- .../cuml/dask/neighbors/nearest_neighbors.py | 14 ++-- python/cuml/cuml/datasets/arima.pyx | 7 +- python/cuml/cuml/datasets/regression.pyx | 8 ++- .../cuml/decomposition/incremental_pca.py | 13 ++-- python/cuml/cuml/decomposition/pca.pyx | 14 ++-- python/cuml/cuml/decomposition/tsvd.pyx | 14 ++-- .../cuml/ensemble/randomforestclassifier.py | 15 ++-- .../cuml/ensemble/randomforestregressor.py | 15 ++-- .../cuml/experimental/linear_model/lars.pyx | 14 ++-- python/cuml/cuml/explainer/base.pyx | 14 ++-- python/cuml/cuml/explainer/kernel_shap.pyx | 14 ++-- .../cuml/cuml/explainer/permutation_shap.pyx | 14 ++-- python/cuml/cuml/feature_extraction/_tfidf.py | 14 ++-- python/cuml/cuml/fil/fil.pyx | 41 ++++++----- python/cuml/cuml/internals/base.py | 14 ++-- python/cuml/cuml/kernel_ridge/kernel_ridge.py | 14 ++-- python/cuml/cuml/linear_model/elastic_net.py | 14 ++-- python/cuml/cuml/linear_model/lasso.py | 14 ++-- .../cuml/linear_model/linear_regression.pyx | 14 ++-- .../cuml/linear_model/logistic_regression.py | 14 ++-- .../cuml/linear_model/mbsgd_classifier.py | 14 ++-- .../cuml/cuml/linear_model/mbsgd_regressor.py | 14 ++-- python/cuml/cuml/linear_model/ridge.pyx | 14 ++-- .../cuml/cuml/manifold/spectral_embedding.pyx | 27 ++++--- python/cuml/cuml/manifold/t_sne.pyx | 14 ++-- python/cuml/cuml/manifold/umap/umap.pyx | 15 ++-- .../metrics/cluster/adjusted_rand_index.pyx | 11 ++- .../metrics/cluster/completeness_score.pyx | 13 ++-- python/cuml/cuml/metrics/cluster/entropy.pyx | 13 ++-- .../metrics/cluster/homogeneity_score.pyx | 13 ++-- .../metrics/cluster/mutual_info_score.pyx | 7 +- .../cuml/metrics/cluster/silhouette_score.pyx | 39 +++++------ .../cuml/cuml/metrics/cluster/v_measure.pyx | 13 ++-- python/cuml/cuml/metrics/kl_divergence.pyx | 7 +- python/cuml/cuml/multiclass/multiclass.py | 28 ++++---- python/cuml/cuml/naive_bayes/naive_bayes.py | 70 +++++++++---------- python/cuml/cuml/neighbors/kernel_density.py | 14 ++-- .../cuml/neighbors/kneighbors_classifier.pyx | 14 ++-- .../cuml/neighbors/kneighbors_regressor.pyx | 14 ++-- .../cuml/cuml/neighbors/nearest_neighbors.pyx | 27 ++++--- .../cuml/cuml/preprocessing/LabelEncoder.py | 14 ++-- python/cuml/cuml/preprocessing/encoders.py | 39 +++++------ python/cuml/cuml/preprocessing/label.py | 14 ++-- .../random_projection/random_projection.py | 26 ++++--- python/cuml/cuml/solvers/cd.pyx | 14 ++-- python/cuml/cuml/solvers/qn.pyx | 14 ++-- python/cuml/cuml/solvers/sgd.pyx | 14 ++-- python/cuml/cuml/svm/linear_svc.py | 15 ++-- python/cuml/cuml/svm/linear_svr.py | 14 ++-- python/cuml/cuml/svm/svc.py | 14 ++-- python/cuml/cuml/svm/svr.py | 14 ++-- python/cuml/cuml/tsa/arima.pyx | 14 ++-- python/cuml/cuml/tsa/auto_arima.pyx | 40 +++++------ python/cuml/cuml/tsa/holtwinters.pyx | 14 ++-- python/cuml/cuml/tsa/seasonality.py | 13 ++-- python/cuml/cuml/tsa/stationarity.pyx | 13 ++-- python/cuml/tests/test_base.py | 23 +++--- 70 files changed, 600 insertions(+), 579 deletions(-) diff --git a/python/cuml/cuml/cluster/agglomerative.pyx b/python/cuml/cuml/cluster/agglomerative.pyx index 8392d08624..b5d6af70dd 100644 --- a/python/cuml/cuml/cluster/agglomerative.pyx +++ b/python/cuml/cuml/cluster/agglomerative.pyx @@ -80,13 +80,13 @@ class AgglomerativeClustering(Base, ClusterMixin, CMajorInputTagMixin): Indirectly influences the number of neighbors to use when ``connectivity="knn"``, with ``n_neighbors = log(n_samples) + c``. The default of 15 should suffice for most problems. - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + verbose : int or boolean, default=False Sets logging level. It must be one of `cuml.common.logger.level_*`. See :ref:`verbosity-levels` for more info. diff --git a/python/cuml/cuml/cluster/dbscan.pyx b/python/cuml/cuml/cluster/dbscan.pyx index d802c3c7d6..66e56a1ba8 100644 --- a/python/cuml/cuml/cluster/dbscan.pyx +++ b/python/cuml/cuml/cluster/dbscan.pyx @@ -151,13 +151,13 @@ class DBSCAN(Base, eps : float (default = 0.5) The maximum distance between 2 points such they reside in the same neighborhood. - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + min_samples : int (default = 5) The number of samples in a neighborhood such that this group can be considered as an important core point (including the point itself). diff --git a/python/cuml/cuml/cluster/hdbscan/hdbscan.pyx b/python/cuml/cuml/cluster/hdbscan/hdbscan.pyx index 65d3cdbf1e..fce7551413 100644 --- a/python/cuml/cuml/cluster/hdbscan/hdbscan.pyx +++ b/python/cuml/cuml/cluster/hdbscan/hdbscan.pyx @@ -511,13 +511,13 @@ class HDBSCAN(Base, InteropMixin, ClusterMixin, CMajorInputTagMixin): Parameters ---------- - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. To configure multi-device execution, + please use the `device_ids` parameter instead. alpha : float, optional (default=1.0) A distance scaling parameter as used in robust single linkage. diff --git a/python/cuml/cuml/cluster/kmeans.pyx b/python/cuml/cuml/cluster/kmeans.pyx index 1a30607a99..e62f6c2168 100644 --- a/python/cuml/cuml/cluster/kmeans.pyx +++ b/python/cuml/cuml/cluster/kmeans.pyx @@ -303,13 +303,13 @@ class KMeans(Base, Parameters ---------- - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + n_clusters : int (default = 8) The number of centroids or clusters you want. max_iter : int (default = 300) diff --git a/python/cuml/cuml/cluster/spectral_clustering.pyx b/python/cuml/cuml/cluster/spectral_clustering.pyx index cd9831e7c1..7f50022dcf 100644 --- a/python/cuml/cuml/cluster/spectral_clustering.pyx +++ b/python/cuml/cuml/cluster/spectral_clustering.pyx @@ -109,13 +109,12 @@ def spectral_clustering(X, - 'nearest_neighbors' : construct the affinity matrix by computing a graph of nearest neighbors. - 'precomputed' : interpret ``A`` as a precomputed affinity matrix. - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. Returns ------- @@ -306,13 +305,13 @@ class SpectralClustering(Base): graph of nearest neighbors from the input data. - 'precomputed' : interpret X as a precomputed affinity matrix, where larger values indicate greater similarity between instances. - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + verbose : int or boolean, default=False Sets logging level. It must be one of `cuml.common.logger.level_*`. See :ref:`verbosity-levels` for more info. diff --git a/python/cuml/cuml/dask/cluster/kmeans.py b/python/cuml/cuml/dask/cluster/kmeans.py index ed08dbde68..37bfe2c14e 100644 --- a/python/cuml/cuml/dask/cluster/kmeans.py +++ b/python/cuml/cuml/dask/cluster/kmeans.py @@ -32,14 +32,13 @@ class KMeans(BaseEstimator, DelayedPredictionMixin, DelayedTransformMixin): Parameters ---------- + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. n_clusters : int (default = 8) The number of centroids or clusters you want. max_iter : int (default = 300) diff --git a/python/cuml/cuml/dask/decomposition/pca.py b/python/cuml/cuml/dask/decomposition/pca.py index 2ec11f50bc..ce607a9f56 100644 --- a/python/cuml/cuml/dask/decomposition/pca.py +++ b/python/cuml/cuml/dask/decomposition/pca.py @@ -77,13 +77,13 @@ class PCA( Parameters ---------- - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + n_components : int (default = 1) The number of top K singular vectors / values you want. Must be <= number(columns). diff --git a/python/cuml/cuml/dask/decomposition/tsvd.py b/python/cuml/cuml/dask/decomposition/tsvd.py index a48c1a2eb6..d1473c1615 100644 --- a/python/cuml/cuml/dask/decomposition/tsvd.py +++ b/python/cuml/cuml/dask/decomposition/tsvd.py @@ -64,13 +64,13 @@ class TruncatedSVD( Parameters ---------- - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + n_components : int (default = 1) The number of top K singular vectors / values you want. Must be <= number(columns). diff --git a/python/cuml/cuml/dask/ensemble/randomforestclassifier.py b/python/cuml/cuml/dask/ensemble/randomforestclassifier.py index 345c1f5035..b3e1d8015f 100755 --- a/python/cuml/cuml/dask/ensemble/randomforestclassifier.py +++ b/python/cuml/cuml/dask/ensemble/randomforestclassifier.py @@ -50,13 +50,13 @@ class RandomForestClassifier( ---------- n_estimators : int (default = 100) total number of trees in the forest (not per-worker) - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + split_criterion : int or string (default = ``0`` (``'gini'``)) The criterion used to split nodes.\n * ``0`` or ``'gini'`` for gini impurity diff --git a/python/cuml/cuml/dask/ensemble/randomforestregressor.py b/python/cuml/cuml/dask/ensemble/randomforestregressor.py index 989622eab8..70af704ca3 100755 --- a/python/cuml/cuml/dask/ensemble/randomforestregressor.py +++ b/python/cuml/cuml/dask/ensemble/randomforestregressor.py @@ -41,13 +41,13 @@ class RandomForestRegressor( ---------- n_estimators : int (default = 100) total number of trees in the forest (not per-worker) - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + split_criterion : int or string (default = ``2`` (``'mse'``)) The criterion used to split nodes.\n * ``0`` or ``'gini'`` for gini impurity diff --git a/python/cuml/cuml/dask/linear_model/elastic_net.py b/python/cuml/cuml/dask/linear_model/elastic_net.py index 79f681980c..dfe1b16029 100644 --- a/python/cuml/cuml/dask/linear_model/elastic_net.py +++ b/python/cuml/cuml/dask/linear_model/elastic_net.py @@ -46,13 +46,13 @@ class ElasticNet(BaseEstimator): rather than looping over features sequentially by default. This (setting to 'random') often leads to significantly faster convergence especially when tol is higher than 1e-4. - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + output_type : {'input', 'array', 'dataframe', 'series', 'df_obj', \ 'numba', 'cupy', 'numpy', 'cudf', 'pandas'}, default=None Return results and set estimator attributes to the indicated output diff --git a/python/cuml/cuml/dask/neighbors/kneighbors_classifier.py b/python/cuml/cuml/dask/neighbors/kneighbors_classifier.py index fb71b1932a..3c53350249 100644 --- a/python/cuml/cuml/dask/neighbors/kneighbors_classifier.py +++ b/python/cuml/cuml/dask/neighbors/kneighbors_classifier.py @@ -39,13 +39,13 @@ class KNeighborsClassifier(NearestNeighbors): of this value will vary for different layouts and index to query ratios, but it will require `batch_size * n_features * 4` bytes of additional memory on each worker hosting index partitions. - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + verbose : int or boolean, default=False Sets logging level. It must be one of `cuml.common.logger.level_*`. See :ref:`verbosity-levels` for more info. diff --git a/python/cuml/cuml/dask/neighbors/kneighbors_regressor.py b/python/cuml/cuml/dask/neighbors/kneighbors_regressor.py index 69a0670be9..f877e59d32 100644 --- a/python/cuml/cuml/dask/neighbors/kneighbors_regressor.py +++ b/python/cuml/cuml/dask/neighbors/kneighbors_regressor.py @@ -36,13 +36,13 @@ class KNeighborsRegressor(NearestNeighbors): of this value will vary for different layouts and index to query ratios, but it will require `batch_size * n_features * 4` bytes of additional memory on each worker hosting index partitions. - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + verbose : int or boolean, default=False Sets logging level. It must be one of `cuml.common.logger.level_*`. See :ref:`verbosity-levels` for more info. diff --git a/python/cuml/cuml/dask/neighbors/nearest_neighbors.py b/python/cuml/cuml/dask/neighbors/nearest_neighbors.py index ee22e51b42..75df26f35e 100644 --- a/python/cuml/cuml/dask/neighbors/nearest_neighbors.py +++ b/python/cuml/cuml/dask/neighbors/nearest_neighbors.py @@ -31,13 +31,13 @@ class NearestNeighbors(BaseEstimator): of this value will vary for different layouts and index to query ratios, but it will require `batch_size * n_features * 4` bytes of additional memory on each worker hosting index partitions. - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + verbose : int or boolean, default=False Sets logging level. It must be one of `cuml.common.logger.level_*`. See :ref:`verbosity-levels` for more info. diff --git a/python/cuml/cuml/datasets/arima.pyx b/python/cuml/cuml/datasets/arima.pyx index aeb1279d5d..2a9e55b9d3 100644 --- a/python/cuml/cuml/datasets/arima.pyx +++ b/python/cuml/cuml/datasets/arima.pyx @@ -82,9 +82,12 @@ def make_arima(batch_size=1000, n_obs=100, order=(1, 1, 1), dtype: string or numpy dtype (default: 'single') Type of the data. Possible values: float32, float64, 'single', 'float' or 'double' + handle : cuml.Handle or None, default=None - handle: cuml.Handle - If it is None, a new one is created just for this function call + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. Returns ------- diff --git a/python/cuml/cuml/datasets/regression.pyx b/python/cuml/cuml/datasets/regression.pyx index 1fb76a295a..983cb92848 100644 --- a/python/cuml/cuml/datasets/regression.pyx +++ b/python/cuml/cuml/datasets/regression.pyx @@ -138,8 +138,12 @@ def make_regression( dtype: string or numpy dtype (default: 'single') Type of the data. Possible values: float32, float64, 'single', 'float' or 'double'. - handle: cuml.Handle - If it is None, a new one is created just for this function call + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. Returns ------- diff --git a/python/cuml/cuml/decomposition/incremental_pca.py b/python/cuml/cuml/decomposition/incremental_pca.py index 687e3a71ae..38e0212edf 100644 --- a/python/cuml/cuml/decomposition/incremental_pca.py +++ b/python/cuml/cuml/decomposition/incremental_pca.py @@ -41,14 +41,13 @@ class IncrementalPCA(PCA): Parameters ---------- + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. n_components : int or None, (default=None) Number of components to keep. If `n_components` is ``None``, then `n_components` is set to :py:`min(n_samples, n_features)`. diff --git a/python/cuml/cuml/decomposition/pca.pyx b/python/cuml/cuml/decomposition/pca.pyx index 0005cabde0..65986fdd08 100644 --- a/python/cuml/cuml/decomposition/pca.pyx +++ b/python/cuml/cuml/decomposition/pca.pyx @@ -168,13 +168,13 @@ class PCA(Base, copy : boolean (default = True) If True, then copies data then removes mean from data. False might cause data to be overwritten with its mean centered version. - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + iterated_power : int (default = 15) Used in Jacobi solver. The more iterations, the more accurate, but slower. diff --git a/python/cuml/cuml/decomposition/tsvd.pyx b/python/cuml/cuml/decomposition/tsvd.pyx index 5fbcfb2520..ea896723a1 100644 --- a/python/cuml/cuml/decomposition/tsvd.pyx +++ b/python/cuml/cuml/decomposition/tsvd.pyx @@ -142,13 +142,13 @@ class TruncatedSVD(Base, Full uses a eigendecomposition of the covariance matrix then discards components. Jacobi is much faster as it iteratively corrects, but is less accurate. - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + n_components : int (default = 1) The number of top K singular vectors / values you want. Must be <= number(columns). diff --git a/python/cuml/cuml/ensemble/randomforestclassifier.py b/python/cuml/cuml/ensemble/randomforestclassifier.py index e12ad33be9..9919091946 100644 --- a/python/cuml/cuml/ensemble/randomforestclassifier.py +++ b/python/cuml/cuml/ensemble/randomforestclassifier.py @@ -122,13 +122,14 @@ class RandomForestClassifier(BaseRandomForestModel, ClassifierMixin): accuracy. Only available if ``bootstrap=True``. The out-of-bag estimate provides a way to evaluate the model without requiring a separate validation set. The OOB score is computed using accuracy. - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. To configure the number of streams + used please use the `n_streams` parameter instead. + verbose : int or boolean, default=False Sets logging level. It must be one of `cuml.common.logger.level_*`. See :ref:`verbosity-levels` for more info. diff --git a/python/cuml/cuml/ensemble/randomforestregressor.py b/python/cuml/cuml/ensemble/randomforestregressor.py index eeab7d1e52..ce096745d5 100644 --- a/python/cuml/cuml/ensemble/randomforestregressor.py +++ b/python/cuml/cuml/ensemble/randomforestregressor.py @@ -118,13 +118,14 @@ class RandomForestRegressor(BaseRandomForestModel, RegressorMixin): estimate provides a way to evaluate the model without requiring a separate validation set. The OOB score is computed using R² (coefficient of determination). - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. To configure the number of streams + used please use the `n_streams` parameter instead. + verbose : int or boolean, default=False Sets logging level. It must be one of `cuml.common.logger.level_*`. See :ref:`verbosity-levels` for more info. diff --git a/python/cuml/cuml/experimental/linear_model/lars.pyx b/python/cuml/cuml/experimental/linear_model/lars.pyx index e1037712bb..67313a57a5 100644 --- a/python/cuml/cuml/experimental/linear_model/lars.pyx +++ b/python/cuml/cuml/experimental/linear_model/lars.pyx @@ -83,13 +83,13 @@ class Lars(Base, RegressorMixin): The maximum number of coefficients to fit. This gives an upper limit of how many features we select for prediction. This number is also an upper limit of the number of iterations. - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + verbose : int or boolean, default=False Sets logging level. It must be one of `cuml.common.logger.level_*`. See :ref:`verbosity-levels` for more info. diff --git a/python/cuml/cuml/explainer/base.pyx b/python/cuml/cuml/explainer/base.pyx index 4c726e9a29..14a24779ad 100644 --- a/python/cuml/cuml/explainer/base.pyx +++ b/python/cuml/cuml/explainer/base.pyx @@ -64,13 +64,13 @@ class SHAPBase(): (as CuPy arrays), otherwise it will use NumPy arrays to call `model`. Set to True to force the explainer to use GPU data, set to False to force the Explainer to use NumPy data. - handle : pylibraft.common.handle - Specifies the handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + dtype : np.float32 or np.float64 (default = np.float32) Parameter to specify the precision of data to generate to call the model. diff --git a/python/cuml/cuml/explainer/kernel_shap.pyx b/python/cuml/cuml/explainer/kernel_shap.pyx index b49d5ef0ef..09de091112 100644 --- a/python/cuml/cuml/explainer/kernel_shap.pyx +++ b/python/cuml/cuml/explainer/kernel_shap.pyx @@ -102,13 +102,13 @@ class KernelExplainer(SHAPBase): (as CuPy arrays), otherwise it will use NumPy arrays to call `model`. Set to True to force the explainer to use GPU data, set to False to force the Explainer to use NumPy data. - handle : pylibraft.common.handle (default = None) - Specifies the handle that holds internal CUDA state for - computations in this model, a new one is created if it is None. - Most importantly, this specifies the CUDA stream that will be used for - the model's computations, so users can run different models - concurrently in different streams by creating handles in several - streams. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + dtype : np.float32 or np.float64 (default = np.float32) Parameter to specify the precision of data to generate to call the model. diff --git a/python/cuml/cuml/explainer/permutation_shap.pyx b/python/cuml/cuml/explainer/permutation_shap.pyx index 072b087821..78f022eade 100644 --- a/python/cuml/cuml/explainer/permutation_shap.pyx +++ b/python/cuml/cuml/explainer/permutation_shap.pyx @@ -103,13 +103,13 @@ class PermutationExplainer(SHAPBase): (as CuPy arrays), otherwise it will use NumPy arrays to call `model`. Set to True to force the explainer to use GPU data, set to False to force the Explainer to use NumPy data. - handle : pylibraft.common.handle (default = None) - Specifies the handle that holds internal CUDA state for - computations in this model, a new one is created if it is None. - Most importantly, this specifies the CUDA stream that will be used for - the model's computations, so users can run different models - concurrently in different streams by creating handles in several - streams. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + dtype : np.float32 or np.float64 (default = np.float32) Parameter to specify the precision of data to generate to call the model. diff --git a/python/cuml/cuml/feature_extraction/_tfidf.py b/python/cuml/cuml/feature_extraction/_tfidf.py index ca50395c65..52152c15db 100644 --- a/python/cuml/cuml/feature_extraction/_tfidf.py +++ b/python/cuml/cuml/feature_extraction/_tfidf.py @@ -89,13 +89,13 @@ class TfidfTransformer(Base): exactly once. Prevents zero divisions. sublinear_tf : bool, default=False Apply sublinear tf scaling, i.e. replace tf with 1 + log(tf). - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + verbose : int or boolean, default=False Sets logging level. It must be one of `cuml.common.logger.level_*`. See :ref:`verbosity-levels` for more info. diff --git a/python/cuml/cuml/fil/fil.pyx b/python/cuml/cuml/fil/fil.pyx index 4d8e61aa4f..963a02beba 100644 --- a/python/cuml/cuml/fil/fil.pyx +++ b/python/cuml/cuml/fil/fil.pyx @@ -424,13 +424,13 @@ class ForestInference(Base, CMajorInputTagMixin): LightGBM, cuML, Scikit-Learn, or any other forest model framework so long as it can be loaded into a treelite.Model object (See https://treelite.readthedocs.io/en/latest/treelite-api.html). - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + output_type : {'input', 'array', 'dataframe', 'series', 'df_obj', \ 'numba', 'cupy', 'numpy', 'cudf', 'pandas'}, default=None Return results and set estimator attributes to the indicated output @@ -769,9 +769,12 @@ class ForestInference(Base, CMajorInputTagMixin): device_id : int, default=0 For GPU execution, the device on which to load and execute this model. For CPU execution, this value is currently ignored. - handle : pylibraft.common.handle or None - For GPU execution, the RAFT handle containing the stream or stream - pool to use during loading and inference. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. """ if model_type is None: extension = pathlib.Path(path).suffix @@ -879,9 +882,12 @@ class ForestInference(Base, CMajorInputTagMixin): device_id : int, default=0 For GPU execution, the device on which to load and execute this model. For CPU execution, this value is currently ignored. - handle : pylibraft.common.handle or None - For GPU execution, the RAFT handle containing the stream or stream - pool to use during loading and inference. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. """ tl_model = treelite.sklearn.import_model(skl_model) result = cls( @@ -967,9 +973,12 @@ class ForestInference(Base, CMajorInputTagMixin): device_id : int, default=0 For GPU execution, the device on which to load and execute this model. For CPU execution, this value is currently ignored. - handle : pylibraft.common.handle or None - For GPU execution, the RAFT handle containing the stream or stream - pool to use during loading and inference. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. """ return cls( treelite_model=tl_model, diff --git a/python/cuml/cuml/internals/base.py b/python/cuml/cuml/internals/base.py index a31cc9634d..bb4859cfca 100644 --- a/python/cuml/cuml/internals/base.py +++ b/python/cuml/cuml/internals/base.py @@ -118,13 +118,13 @@ class Base(TagsMixin): Parameters ---------- - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + verbose : int or boolean, default=False Sets logging level. It must be one of `cuml.common.logger.level_*`. See :ref:`verbosity-levels` for more info. diff --git a/python/cuml/cuml/kernel_ridge/kernel_ridge.py b/python/cuml/cuml/kernel_ridge/kernel_ridge.py index 56ed66bec8..62b1a935d0 100644 --- a/python/cuml/cuml/kernel_ridge/kernel_ridge.py +++ b/python/cuml/cuml/kernel_ridge/kernel_ridge.py @@ -137,13 +137,13 @@ class KernelRidge(Base, InteropMixin, RegressorMixin): type. If None, the output type set at the module level (`cuml.global_settings.output_type`) will be used. See :ref:`output-data-type-configuration` for more info. - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the - CUDA stream that will be used for the model's computations, so - users can run different models concurrently in different streams - by creating handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + verbose : int or boolean, default=False Sets logging level. It must be one of `cuml.common.logger.level_*`. See :ref:`verbosity-levels` for more info. diff --git a/python/cuml/cuml/linear_model/elastic_net.py b/python/cuml/cuml/linear_model/elastic_net.py index 1fe2230509..46809b74a0 100644 --- a/python/cuml/cuml/linear_model/elastic_net.py +++ b/python/cuml/cuml/linear_model/elastic_net.py @@ -62,13 +62,13 @@ class ElasticNet( features sequentially by default. This (setting to 'random') often leads to significantly faster convergence especially when tol is higher than 1e-4. - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + output_type : {'input', 'array', 'dataframe', 'series', 'df_obj', \ 'numba', 'cupy', 'numpy', 'cudf', 'pandas'}, default=None Return results and set estimator attributes to the indicated output diff --git a/python/cuml/cuml/linear_model/lasso.py b/python/cuml/cuml/linear_model/lasso.py index aa725102a4..a42c173eb7 100644 --- a/python/cuml/cuml/linear_model/lasso.py +++ b/python/cuml/cuml/linear_model/lasso.py @@ -43,13 +43,13 @@ class Lasso(ElasticNet): rather than looping over features sequentially by default. This (setting to 'random') often leads to significantly faster convergence especially when tol is higher than 1e-4. - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + output_type : {'input', 'array', 'dataframe', 'series', 'df_obj', \ 'numba', 'cupy', 'numpy', 'cudf', 'pandas'}, default=None Return results and set estimator attributes to the indicated output diff --git a/python/cuml/cuml/linear_model/linear_regression.pyx b/python/cuml/cuml/linear_model/linear_regression.pyx index 17b1b4543c..89f0518dc1 100644 --- a/python/cuml/cuml/linear_model/linear_regression.pyx +++ b/python/cuml/cuml/linear_model/linear_regression.pyx @@ -167,13 +167,13 @@ class LinearRegression(Base, memory usage. This represents a change in behavior from previous versions. With `copy_X=False` a copy might still be created if necessary. - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + verbose : int or boolean, default=False Sets logging level. It must be one of `cuml.common.logger.level_*`. See :ref:`verbosity-levels` for more info. diff --git a/python/cuml/cuml/linear_model/logistic_regression.py b/python/cuml/cuml/linear_model/logistic_regression.py index 04d01e0bc4..9cf79d8405 100644 --- a/python/cuml/cuml/linear_model/logistic_regression.py +++ b/python/cuml/cuml/linear_model/logistic_regression.py @@ -92,13 +92,13 @@ class LogisticRegression( verbose : int or boolean, default=False Sets logging level. It must be one of `cuml.common.logger.level_*`. See :ref:`verbosity-levels` for more info. - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + output_type : {'input', 'array', 'dataframe', 'series', 'df_obj', \ 'numba', 'cupy', 'numpy', 'cudf', 'pandas'}, default=None Return results and set estimator attributes to the indicated output diff --git a/python/cuml/cuml/linear_model/mbsgd_classifier.py b/python/cuml/cuml/linear_model/mbsgd_classifier.py index f1a1fa0bc3..2fac2d5b58 100644 --- a/python/cuml/cuml/linear_model/mbsgd_classifier.py +++ b/python/cuml/cuml/linear_model/mbsgd_classifier.py @@ -81,13 +81,13 @@ class MBSGDClassifier( The old learning rate is generally divided by 5 n_iter_no_change : int (default = 5) the number of epochs to train without any improvement in the model - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + verbose : int or boolean, default=False Sets logging level. It must be one of `cuml.common.logger.level_*`. See :ref:`verbosity-levels` for more info. diff --git a/python/cuml/cuml/linear_model/mbsgd_regressor.py b/python/cuml/cuml/linear_model/mbsgd_regressor.py index 73e6994a5e..319575da75 100644 --- a/python/cuml/cuml/linear_model/mbsgd_regressor.py +++ b/python/cuml/cuml/linear_model/mbsgd_regressor.py @@ -71,13 +71,13 @@ class MBSGDRegressor( The old learning rate is generally divided by 5 n_iter_no_change : int (default = 5) the number of epochs to train without any improvement in the model - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + verbose : int or boolean, default=False Sets logging level. It must be one of `cuml.common.logger.level_*`. See :ref:`verbosity-levels` for more info. diff --git a/python/cuml/cuml/linear_model/ridge.pyx b/python/cuml/cuml/linear_model/ridge.pyx index 917ca2f530..a12f6364c0 100644 --- a/python/cuml/cuml/linear_model/ridge.pyx +++ b/python/cuml/cuml/linear_model/ridge.pyx @@ -103,13 +103,13 @@ class Ridge(Base, copy_X: bool, default=True If True, X will never be mutated. Setting to False may reduce memory usage, at the cost of potentially mutating X. - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + output_type : {'input', 'array', 'dataframe', 'series', 'df_obj', \ 'numba', 'cupy', 'numpy', 'cudf', 'pandas'}, default=None Return results and set estimator attributes to the indicated output diff --git a/python/cuml/cuml/manifold/spectral_embedding.pyx b/python/cuml/cuml/manifold/spectral_embedding.pyx index 7343290ed2..01310fc466 100644 --- a/python/cuml/cuml/manifold/spectral_embedding.pyx +++ b/python/cuml/cuml/manifold/spectral_embedding.pyx @@ -110,13 +110,12 @@ def spectral_embedding(A, should be True as the first eigenvector should be constant vector for connected graph, but for spectral clustering, this should be kept as False to retain the first eigenvector. - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. Returns ------- @@ -286,13 +285,13 @@ class SpectralEmbedding(Base, InteropMixin, CMajorInputTagMixin): n_neighbors : int or None, default=2 Number of nearest neighbors for nearest_neighbors graph building. If None, n_neighbors will be set to max(n_samples/10, 1). - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + verbose : int or boolean, default=False Sets logging level. It must be one of `cuml.common.logger.level_*`. See :ref:`verbosity-levels` for more info. diff --git a/python/cuml/cuml/manifold/t_sne.pyx b/python/cuml/cuml/manifold/t_sne.pyx index bf05a25593..863e12191b 100644 --- a/python/cuml/cuml/manifold/t_sne.pyx +++ b/python/cuml/cuml/manifold/t_sne.pyx @@ -347,13 +347,13 @@ class TSNE(Base, the precomputation of the KNN outside of TSNE and also allows the use of a custom distance function. This function should match the metric used to train the TSNE embeedings. - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + output_type : {'input', 'array', 'dataframe', 'series', 'df_obj', \ 'numba', 'cupy', 'numpy', 'cudf', 'pandas'}, default=None Return results and set estimator attributes to the indicated output diff --git a/python/cuml/cuml/manifold/umap/umap.pyx b/python/cuml/cuml/manifold/umap/umap.pyx index 76466937f4..d390776604 100644 --- a/python/cuml/cuml/manifold/umap/umap.pyx +++ b/python/cuml/cuml/manifold/umap/umap.pyx @@ -551,13 +551,14 @@ class UMAP(Base, InteropMixin, CMajorInputTagMixin, SparseInputTagMixin): def on_train_end(self, embeddings): print(embeddings.copy_to_host()) - handle : cuml.Handle or pylibraft.common.DeviceResourcesSNMG - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. To configure multi-device execution, + please use the `device_ids` parameter instead. + verbose : int or boolean, default=False Sets logging level. It must be one of `cuml.common.logger.level_*`. See :ref:`verbosity-levels` for more info. diff --git a/python/cuml/cuml/metrics/cluster/adjusted_rand_index.pyx b/python/cuml/cuml/metrics/cluster/adjusted_rand_index.pyx index 5a4e3a272d..03cb886885 100644 --- a/python/cuml/cuml/metrics/cluster/adjusted_rand_index.pyx +++ b/python/cuml/cuml/metrics/cluster/adjusted_rand_index.pyx @@ -27,11 +27,16 @@ def adjusted_rand_score(labels_true, labels_pred, handle=None, Parameters ---------- - labels_true : Ground truth labels to be used as a reference + labels_true : Ground truth labels to be used as a reference - labels_pred : Array of predicted labels used to evaluate the model + labels_pred : Array of predicted labels used to evaluate the model - handle : cuml.Handle + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. Returns ------- diff --git a/python/cuml/cuml/metrics/cluster/completeness_score.pyx b/python/cuml/cuml/metrics/cluster/completeness_score.pyx index abebe88a25..77909d8109 100644 --- a/python/cuml/cuml/metrics/cluster/completeness_score.pyx +++ b/python/cuml/cuml/metrics/cluster/completeness_score.pyx @@ -44,13 +44,12 @@ def cython_completeness_score(labels_true, labels_pred, handle=None) -> float: The ground truth labels (ints) of the test dataset. Acceptable formats: cuDF DataFrame, NumPy ndarray, Numba device ndarray, cuda array interface compliant array like CuPy - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. Returns ------- diff --git a/python/cuml/cuml/metrics/cluster/entropy.pyx b/python/cuml/cuml/metrics/cluster/entropy.pyx index 2696ceee32..5e372bdb43 100644 --- a/python/cuml/cuml/metrics/cluster/entropy.pyx +++ b/python/cuml/cuml/metrics/cluster/entropy.pyx @@ -35,13 +35,12 @@ def cython_entropy(clustering, base=None, handle=None) -> float: probability for tail, the clustering could be [0, 0, 1]. base: float, optional The logarithmic base to use, defaults to e (natural logarithm). - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. Returns ------- diff --git a/python/cuml/cuml/metrics/cluster/homogeneity_score.pyx b/python/cuml/cuml/metrics/cluster/homogeneity_score.pyx index d8892c52b7..c187e68407 100644 --- a/python/cuml/cuml/metrics/cluster/homogeneity_score.pyx +++ b/python/cuml/cuml/metrics/cluster/homogeneity_score.pyx @@ -44,13 +44,12 @@ def cython_homogeneity_score(labels_true, labels_pred, handle=None) -> float: The ground truth labels (ints) of the test dataset. Acceptable formats: cuDF DataFrame, NumPy ndarray, Numba device ndarray, cuda array interface compliant array like CuPy - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. Returns ------- diff --git a/python/cuml/cuml/metrics/cluster/mutual_info_score.pyx b/python/cuml/cuml/metrics/cluster/mutual_info_score.pyx index 107fc5608d..16ba63667c 100644 --- a/python/cuml/cuml/metrics/cluster/mutual_info_score.pyx +++ b/python/cuml/cuml/metrics/cluster/mutual_info_score.pyx @@ -40,7 +40,12 @@ def cython_mutual_info_score(labels_true, labels_pred, handle=None) -> float: Parameters ---------- - handle : cuml.Handle + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. labels_pred : array-like (device or host) shape = (n_samples,) A clustering of the data (ints) into disjoint subsets. Acceptable formats: cuDF DataFrame, NumPy ndarray, Numba device diff --git a/python/cuml/cuml/metrics/cluster/silhouette_score.pyx b/python/cuml/cuml/metrics/cluster/silhouette_score.pyx index 8aaf910ca4..6397176633 100644 --- a/python/cuml/cuml/metrics/cluster/silhouette_score.pyx +++ b/python/cuml/cuml/metrics/cluster/silhouette_score.pyx @@ -66,13 +66,12 @@ def _silhouette_coeff( If None, chunksize will automatically be set to 40000, which through experiments has proved to be a safe number for the computation to run on a GPU with 16 GB VRAM. - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. """ handle = get_handle(handle=handle) cdef handle_t *handle_ = handle.getHandle() @@ -175,13 +174,12 @@ def cython_silhouette_score( If None, chunksize will automatically be set to 40000, which through experiments has proved to be a safe number for the computation to run on a GPU with 16 GB VRAM. - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. """ return _silhouette_coeff( @@ -221,13 +219,12 @@ def cython_silhouette_samples( If None, chunksize will automatically be set to 40000, which through experiments has proved to be a safe number for the computation to run on a GPU with 16 GB VRAM. - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. """ sil_scores = cp.empty((X.shape[0],), dtype=X.dtype) diff --git a/python/cuml/cuml/metrics/cluster/v_measure.pyx b/python/cuml/cuml/metrics/cluster/v_measure.pyx index aed96d6892..a4ed4011dc 100644 --- a/python/cuml/cuml/metrics/cluster/v_measure.pyx +++ b/python/cuml/cuml/metrics/cluster/v_measure.pyx @@ -52,13 +52,12 @@ def cython_v_measure(labels_true, labels_pred, beta=1.0, handle=None) -> float: If ``beta`` is greater than 1, ``completeness`` is weighted more strongly in the calculation. If ``beta`` is less than 1, ``homogeneity`` is weighted more strongly. - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. Returns ------- diff --git a/python/cuml/cuml/metrics/kl_divergence.pyx b/python/cuml/cuml/metrics/kl_divergence.pyx index 4373a1c528..7fa3cb829d 100644 --- a/python/cuml/cuml/metrics/kl_divergence.pyx +++ b/python/cuml/cuml/metrics/kl_divergence.pyx @@ -44,7 +44,12 @@ def kl_divergence(P, Q, handle=None, convert_dtype=True): Acceptable formats: cuDF DataFrame, NumPy ndarray, Numba device ndarray, cuda array interface compliant array like CuPy. - handle : cuml.Handle + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. convert_dtype : bool, optional (default = True) When set to True, the method will, convert P and diff --git a/python/cuml/cuml/multiclass/multiclass.py b/python/cuml/cuml/multiclass/multiclass.py index 1461010b56..336d66ab7f 100644 --- a/python/cuml/cuml/multiclass/multiclass.py +++ b/python/cuml/cuml/multiclass/multiclass.py @@ -116,13 +116,13 @@ class OneVsRestClassifier(_BaseMulticlassClassifier): Parameters ---------- estimator : cuML estimator - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + verbose : int or boolean, default=False Sets logging level. It must be one of `cuml.common.logger.level_*`. See :ref:`verbosity-levels` for more info. @@ -171,13 +171,13 @@ class OneVsOneClassifier(_BaseMulticlassClassifier): Parameters ---------- estimator : cuML estimator - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + verbose : int or boolean, default=False Sets logging level. It must be one of `cuml.common.logger.level_*`. See :ref:`verbosity-levels` for more info. diff --git a/python/cuml/cuml/naive_bayes/naive_bayes.py b/python/cuml/cuml/naive_bayes/naive_bayes.py index 1179fb4d82..761bd85e65 100644 --- a/python/cuml/cuml/naive_bayes/naive_bayes.py +++ b/python/cuml/cuml/naive_bayes/naive_bayes.py @@ -318,13 +318,13 @@ class GaussianNB(_BaseNB): type. If None, the output type set at the module level (`cuml.global_settings.output_type`) will be used. See :ref:`output-data-type-configuration` for more info. - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the - CUDA stream that will be used for the model's computations, so - users can run different models concurrently in different streams - by creating handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + verbose : int or boolean, default=False Sets logging level. It must be one of `cuml.common.logger.level_*`. See :ref:`verbosity-levels` for more info. @@ -1047,13 +1047,13 @@ class MultinomialNB(_BaseDiscreteNB): type. If None, the output type set at the module level (`cuml.global_settings.output_type`) will be used. See :ref:`output-data-type-configuration` for more info. - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the - CUDA stream that will be used for the model's computations, so - users can run different models concurrently in different streams - by creating handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + verbose : int or boolean, default=False Sets logging level. It must be one of `cuml.common.logger.level_*`. See :ref:`verbosity-levels` for more info. @@ -1145,13 +1145,13 @@ class BernoulliNB(_BaseDiscreteNB): type. If None, the output type set at the module level (`cuml.global_settings.output_type`) will be used. See :ref:`output-data-type-configuration` for more info. - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the - CUDA stream that will be used for the model's computations, so - users can run different models concurrently in different streams - by creating handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + verbose : int or boolean, default=False Sets logging level. It must be one of `cuml.common.logger.level_*`. See :ref:`verbosity-levels` for more info. @@ -1312,13 +1312,13 @@ class ComplementNB(_BaseDiscreteNB): type. If None, the output type set at the module level (`cuml.global_settings.output_type`) will be used. See :ref:`output-data-type-configuration` for more info. - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the - CUDA stream that will be used for the model's computations, so - users can run different models concurrently in different streams - by creating handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + verbose : int or boolean, default=False Sets logging level. It must be one of `cuml.common.logger.level_*`. See :ref:`verbosity-levels` for more info. @@ -1467,13 +1467,13 @@ class CategoricalNB(_BaseDiscreteNB): type. If None, the output type set at the module level (`cuml.global_settings.output_type`) will be used. See :ref:`output-data-type-configuration` for more info. - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the - CUDA stream that will be used for the model's computations, so - users can run different models concurrently in different streams - by creating handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + verbose : int or boolean, default=False Sets logging level. It must be one of `cuml.common.logger.level_*`. See :ref:`verbosity-levels` for more info. diff --git a/python/cuml/cuml/neighbors/kernel_density.py b/python/cuml/cuml/neighbors/kernel_density.py index 5ddf1476b3..5986a3621b 100644 --- a/python/cuml/cuml/neighbors/kernel_density.py +++ b/python/cuml/cuml/neighbors/kernel_density.py @@ -161,13 +161,13 @@ class KernelDensity(Base, InteropMixin): type. If None, the output type set at the module level (`cuml.global_settings.output_type`) will be used. See :ref:`output-data-type-configuration` for more info. - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the - CUDA stream that will be used for the model's computations, so - users can run different models concurrently in different streams - by creating handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + verbose : int or boolean, default=False Sets logging level. It must be one of `cuml.common.logger.level_*`. See :ref:`verbosity-levels` for more info. diff --git a/python/cuml/cuml/neighbors/kneighbors_classifier.pyx b/python/cuml/cuml/neighbors/kneighbors_classifier.pyx index c3b0cc1df4..abf6d4532d 100644 --- a/python/cuml/cuml/neighbors/kneighbors_classifier.pyx +++ b/python/cuml/cuml/neighbors/kneighbors_classifier.pyx @@ -76,13 +76,13 @@ class KNeighborsClassifier(ClassifierMixin, - [callable] : a user-defined function which accepts an array of distances, and returns an array of the same shape containing the weights. - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + verbose : int or boolean, default=False Sets logging level. It must be one of `cuml.common.logger.level_*`. See :ref:`verbosity-levels` for more info. diff --git a/python/cuml/cuml/neighbors/kneighbors_regressor.pyx b/python/cuml/cuml/neighbors/kneighbors_regressor.pyx index aecbe74cdc..61c16c84f8 100644 --- a/python/cuml/cuml/neighbors/kneighbors_regressor.pyx +++ b/python/cuml/cuml/neighbors/kneighbors_regressor.pyx @@ -75,13 +75,13 @@ class KNeighborsRegressor(RegressorMixin, FMajorInputTagMixin, NearestNeighbors) - [callable] : a user-defined function which accepts an array of distances, and returns an array of the same shape containing the weights. - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + verbose : int or boolean, default=False Sets logging level. It must be one of `cuml.common.logger.level_*`. See :ref:`verbosity-levels` for more info. diff --git a/python/cuml/cuml/neighbors/nearest_neighbors.pyx b/python/cuml/cuml/neighbors/nearest_neighbors.pyx index e32a6c0be5..3a44cde97c 100644 --- a/python/cuml/cuml/neighbors/nearest_neighbors.pyx +++ b/python/cuml/cuml/neighbors/nearest_neighbors.pyx @@ -447,13 +447,13 @@ class NearestNeighbors(Base, verbose : int or boolean, default=False Sets logging level. It must be one of `cuml.common.logger.level_*`. See :ref:`verbosity-levels` for more info. - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + algorithm : string (default='auto') The query algorithm to use. Valid options are: @@ -1131,13 +1131,12 @@ def kneighbors_graph(X=None, n_neighbors=5, mode='connectivity', verbose=False, Sets logging level. It must be one of `cuml.common.logger.level_*`. See :ref:`verbosity-levels` for more info. - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. algorithm : string (default='brute') The query algorithm to use. Valid options are: diff --git a/python/cuml/cuml/preprocessing/LabelEncoder.py b/python/cuml/cuml/preprocessing/LabelEncoder.py index eeade9fbfc..a6deb016e4 100644 --- a/python/cuml/cuml/preprocessing/LabelEncoder.py +++ b/python/cuml/cuml/preprocessing/LabelEncoder.py @@ -47,13 +47,13 @@ class LabelEncoder(Base): is present during transform (default is to raise). When this parameter is set to 'ignore' and an unknown category is encountered during transform or inverse transform, the resulting encoding will be null. - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + verbose : int or boolean, default=False Sets logging level. It must be one of `cuml.common.logger.level_*`. See :ref:`verbosity-levels` for more info. diff --git a/python/cuml/cuml/preprocessing/encoders.py b/python/cuml/cuml/preprocessing/encoders.py index 93b8984ca7..cab17dcd14 100644 --- a/python/cuml/cuml/preprocessing/encoders.py +++ b/python/cuml/cuml/preprocessing/encoders.py @@ -48,14 +48,13 @@ class BaseEncoder(Base, CheckFeaturesMixIn): Parameters ---------- + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. verbose : int or boolean, default=False Sets logging level. It must be one of `cuml.common.logger.level_*`. See :ref:`verbosity-levels` for more info. @@ -203,13 +202,13 @@ class OneHotEncoder(BaseEncoder): transform, the resulting one-hot encoded columns for this feature will be all zeros. In the inverse transform, an unknown category will be denoted as None. - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + verbose : int or boolean, default=False Sets logging level. It must be one of `cuml.common.logger.level_*`. See :ref:`verbosity-levels` for more info. @@ -639,13 +638,13 @@ def __init__( to 'ignore' and an unknown category is encountered during transform, the resulting encoded value would be null when output type is cudf dataframe. - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for computations in - this model. Most importantly, this specifies the CUDA stream that will be - used for the model's computations, so users can run different models - concurrently in different streams by creating handles in several streams. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. - If it is None, a new one is created. verbose : int or boolean, default=False Sets logging level. It must be one of `cuml.common.logger.level_*`. See :ref:`verbosity-levels` for more info. diff --git a/python/cuml/cuml/preprocessing/label.py b/python/cuml/cuml/preprocessing/label.py index 8b888f3c4f..9e3e2c023a 100644 --- a/python/cuml/cuml/preprocessing/label.py +++ b/python/cuml/cuml/preprocessing/label.py @@ -78,13 +78,13 @@ class LabelBinarizer(Base): label to be used as the positive binary label sparse_output : bool (default=False) whether to return sparse arrays for transformed output - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + verbose : int or boolean, default=False Sets logging level. It must be one of `cuml.common.logger.level_*`. See :ref:`verbosity-levels` for more info. diff --git a/python/cuml/cuml/random_projection/random_projection.py b/python/cuml/cuml/random_projection/random_projection.py index b6ea3a7de5..318feb5b7c 100644 --- a/python/cuml/cuml/random_projection/random_projection.py +++ b/python/cuml/cuml/random_projection/random_projection.py @@ -201,13 +201,12 @@ class GaussianRandomProjection(_BaseRandomProjection): (`cuml.global_settings.output_type`) will be used. See :ref:`output-data-type-configuration` for more info. - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the - CUDA stream that will be used for the model's computations, so - users can run different models concurrently in different streams - by creating handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. verbose : int or boolean, default=False Sets logging level. It must be one of `cuml.common.logger.level_*`. @@ -317,13 +316,12 @@ class SparseRandomProjection(_BaseRandomProjection): (`cuml.global_settings.output_type`) will be used. See :ref:`output-data-type-configuration` for more info. - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the - CUDA stream that will be used for the model's computations, so - users can run different models concurrently in different streams - by creating handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. verbose : int or boolean, default=False Sets logging level. It must be one of `cuml.common.logger.level_*`. diff --git a/python/cuml/cuml/solvers/cd.pyx b/python/cuml/cuml/solvers/cd.pyx index e1440d2c07..29bd903ab2 100644 --- a/python/cuml/cuml/solvers/cd.pyx +++ b/python/cuml/cuml/solvers/cd.pyx @@ -258,13 +258,13 @@ class CD(Base, FMajorInputTagMixin): than looping over features sequentially by default. This (setting to 'True') often leads to significantly faster convergence especially when tol is higher than 1e-4. - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + verbose : int or boolean, default=False Sets logging level. It must be one of `cuml.common.logger.level_*`. See :ref:`verbosity-levels` for more info. diff --git a/python/cuml/cuml/solvers/qn.pyx b/python/cuml/cuml/solvers/qn.pyx index 2ee39757e3..f836acf48e 100644 --- a/python/cuml/cuml/solvers/qn.pyx +++ b/python/cuml/cuml/solvers/qn.pyx @@ -420,13 +420,13 @@ class QN(Base): lbfgs_memory: int (default = 5) Rank of the lbfgs inverse-Hessian approximation. Method will use O(lbfgs_memory * D) memory. - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + verbose : int or boolean, default=False Sets logging level. It must be one of `cuml.common.logger.level_*`. See :ref:`verbosity-levels` for more info. diff --git a/python/cuml/cuml/solvers/sgd.pyx b/python/cuml/cuml/solvers/sgd.pyx index 6aee2de646..26e3fdfada 100644 --- a/python/cuml/cuml/solvers/sgd.pyx +++ b/python/cuml/cuml/solvers/sgd.pyx @@ -326,13 +326,13 @@ class SGD(Base, FMajorInputTagMixin): The old learning rate is generally divide by 5 n_iter_no_change : int (default = 5) The number of epochs to train without any improvement in the model - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + output_type : {'input', 'array', 'dataframe', 'series', 'df_obj', \ 'numba', 'cupy', 'numpy', 'cudf', 'pandas'}, default=None Return results and set estimator attributes to the indicated output diff --git a/python/cuml/cuml/svm/linear_svc.py b/python/cuml/cuml/svm/linear_svc.py index a349e0bc88..20fa244c04 100644 --- a/python/cuml/cuml/svm/linear_svc.py +++ b/python/cuml/cuml/svm/linear_svc.py @@ -74,13 +74,14 @@ class LinearSVC(Base, InteropMixin, LinearClassifierMixin, ClassifierMixin): Number of parallel streams used for fitting. multi_class : {'ovr'}, default='ovr' Multiclass classification strategy. Currently only 'ovr' is supported. - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. To configure the number of streams + used please use the `n_streams` parameter instead. + verbose : int or boolean, default=False Sets logging level. It must be one of `cuml.common.logger.level_*`. See :ref:`verbosity-levels` for more info. diff --git a/python/cuml/cuml/svm/linear_svr.py b/python/cuml/cuml/svm/linear_svr.py index bf4fa16907..5d78ba516a 100644 --- a/python/cuml/cuml/svm/linear_svr.py +++ b/python/cuml/cuml/svm/linear_svr.py @@ -58,13 +58,13 @@ class LinearSVR(Base, InteropMixin, LinearPredictMixin, RegressorMixin): lbfgs_memory : int, default=5 Number of vectors approximating the hessian for the underlying QN solver (l-bfgs). - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + verbose : int or boolean, default=False Sets logging level. It must be one of `cuml.common.logger.level_*`. See :ref:`verbosity-levels` for more info. diff --git a/python/cuml/cuml/svm/svc.py b/python/cuml/cuml/svm/svc.py index c318cfdd79..9d51f4dc43 100644 --- a/python/cuml/cuml/svm/svc.py +++ b/python/cuml/cuml/svm/svc.py @@ -55,13 +55,13 @@ class SVC(SVMBase, ClassifierMixin): Parameters ---------- - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + C : float (default = 1.0) Penalty parameter C kernel : string (default='rbf') diff --git a/python/cuml/cuml/svm/svr.py b/python/cuml/cuml/svm/svr.py index 7a0293a575..c606ce72f3 100644 --- a/python/cuml/cuml/svm/svr.py +++ b/python/cuml/cuml/svm/svr.py @@ -19,13 +19,13 @@ class SVR(SVMBase, RegressorMixin): Parameters ---------- - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + C : float (default = 1.0) Penalty parameter C kernel : string (default='rbf') diff --git a/python/cuml/cuml/tsa/arima.pyx b/python/cuml/cuml/tsa/arima.pyx index 87c69aa8c3..9e45a341dc 100644 --- a/python/cuml/cuml/tsa/arima.pyx +++ b/python/cuml/cuml/tsa/arima.pyx @@ -174,13 +174,13 @@ class ARIMA(Base): Note: that forecasts are always for the original series, whereas statsmodels computes forecasts for the differenced series when simple_differencing is True. - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + verbose : int or boolean, default=False Sets logging level. It must be one of `cuml.common.logger.level_*`. See :ref:`verbosity-levels` for more info. diff --git a/python/cuml/cuml/tsa/auto_arima.pyx b/python/cuml/cuml/tsa/auto_arima.pyx index 58c513fa06..dcbe1da0e4 100644 --- a/python/cuml/cuml/tsa/auto_arima.pyx +++ b/python/cuml/cuml/tsa/auto_arima.pyx @@ -106,13 +106,13 @@ class AutoARIMA(Base): The time series data, assumed to have each time series in columns. Acceptable formats: cuDF DataFrame, cuDF Series, NumPy ndarray, Numba device ndarray, cuda array interface compliant array like CuPy. - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + simple_differencing: bool or int, default=True If True, the data is differenced before being passed to the Kalman filter. If False, differencing is part of the state-space model. @@ -575,13 +575,12 @@ def _divide_by_mask(original, mask, batch_id, handle=None): Boolean mask: False for the 1st sub-batch and True for the second batch_id : CumlArray (int) Integer array to track the id of each member in the initial batch - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. Returns ------- @@ -699,13 +698,12 @@ def _divide_by_min(original, metrics, batch_id, handle=None): Matrix of shape (batch_size, n_sub) containing the metrics to minimize batch_id : CumlArray (int) Integer array to track the id of each member in the initial batch - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. Returns ------- diff --git a/python/cuml/cuml/tsa/holtwinters.pyx b/python/cuml/cuml/tsa/holtwinters.pyx index 819842f534..1ef1764b9c 100644 --- a/python/cuml/cuml/tsa/holtwinters.pyx +++ b/python/cuml/cuml/tsa/holtwinters.pyx @@ -140,13 +140,13 @@ class ExponentialSmoothing(Base): eps : np.number > 0 (default=2.24e-3) The accuracy to which gradient descent should achieve. Note that changing this value may affect the forecasted results. - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. + verbose : int or boolean, default=False Sets logging level. It must be one of `cuml.common.logger.level_*`. See :ref:`verbosity-levels` for more info. diff --git a/python/cuml/cuml/tsa/seasonality.py b/python/cuml/cuml/tsa/seasonality.py index 6215e488c6..cbe62ff556 100644 --- a/python/cuml/cuml/tsa/seasonality.py +++ b/python/cuml/cuml/tsa/seasonality.py @@ -39,13 +39,12 @@ def seas_test(y, s, handle=None, convert_dtype=True) -> CumlArray: Numba device ndarray, cuda array interface compliant array like CuPy. s: integer Seasonal period (s > 1) - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. Returns ------- diff --git a/python/cuml/cuml/tsa/stationarity.pyx b/python/cuml/cuml/tsa/stationarity.pyx index 2213b620eb..5fc07eede6 100644 --- a/python/cuml/cuml/tsa/stationarity.pyx +++ b/python/cuml/cuml/tsa/stationarity.pyx @@ -52,13 +52,12 @@ def kpss_test(y, d=0, D=0, s=0, pval_threshold=0.05, Seasonal period if D > 0 pval_threshold : float The p-value threshold above which a series is considered stationary. - handle : cuml.Handle - Specifies the cuml.handle that holds internal CUDA state for - computations in this model. Most importantly, this specifies the CUDA - stream that will be used for the model's computations, so users can - run different models concurrently in different streams by creating - handles in several streams. - If it is None, a new one is created. + handle : cuml.Handle or None, default=None + + .. deprecated:: 26.02 + The `handle` argument was deprecated in 26.02 and will be removed + in 26.04. There's no need to pass in a handle, cuml now manages + this resource automatically. Returns ------- diff --git a/python/cuml/tests/test_base.py b/python/cuml/tests/test_base.py index 50df323b9c..8b43943c33 100644 --- a/python/cuml/tests/test_base.py +++ b/python/cuml/tests/test_base.py @@ -116,16 +116,21 @@ def get_param_doc(param_doc_obj, name: str): base_item_doc = get_param_doc(base_doc_params, name) - if not ( - found_doc.type.startswith("cuml.Handle") - and klass == cuml.manifold.umap.UMAP - ): - # Ensure the docstring is identical - assert found_doc.type == base_item_doc.type, ( - "Docstring mismatch for {}".format(name) - ) + assert found_doc.type == base_item_doc.type, ( + f"Docstring mismatch for {name}" + ) - assert " ".join(found_doc.desc) == " ".join(base_item_doc.desc) + found = " ".join(found_doc.desc) + expected = " ".join(base_item_doc.desc) + + if name == "handle": + # Handle may have a trailing suffix, class dependent + assert found.startswith(expected), ( + f"Docstring mismatch for {name}" + ) + else: + # Exact match + assert found == expected, f"Docstring mismatch for {name}" @pytest.mark.parametrize("child_class", list(all_base_children.keys())) From e95631866cdbc898d9a76df1dae288eb90532ec8 Mon Sep 17 00:00:00 2001 From: Jim Crist-Harif Date: Thu, 18 Dec 2025 14:34:17 -0600 Subject: [PATCH 16/21] Remove unused `cuml.testing.utils.get_handle` --- python/cuml/cuml/testing/utils.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/python/cuml/cuml/testing/utils.py b/python/cuml/cuml/testing/utils.py index 762524f535..a7c5235da7 100644 --- a/python/cuml/cuml/testing/utils.py +++ b/python/cuml/cuml/testing/utils.py @@ -14,10 +14,8 @@ from cudf.pandas import LOADED as cudf_pandas_active from numba import cuda from numba.cuda.cudadrv.devicearray import DeviceNDArray -from pylibraft.common.cuda import Stream from sklearn.metrics import brier_score_loss, mean_squared_error -import cuml from cuml.internals.base import Base from cuml.internals.input_utils import input_to_cuml_array, is_array_like from cuml.internals.mem_type import MemoryType @@ -245,14 +243,6 @@ def sqnorm(x): # requirement of minimality for core points -def get_handle(use_handle, n_streams=0): - if not use_handle: - return None, None - s = Stream() - h = cuml.Handle(stream=s, n_streams=n_streams) - return h, s - - def unit_param(*args, **kwargs): return pytest.param(*args, **kwargs, marks=pytest.mark.unit) From 7c6ce639260fbfbac1ccd5b1fe4319819607885d Mon Sep 17 00:00:00 2001 From: Jim Crist-Harif Date: Thu, 18 Dec 2025 14:42:43 -0600 Subject: [PATCH 17/21] Deprecate `cuml.Handle` --- python/cuml/cuml/__init__.py | 13 ++++++++++++- python/cuml/tests/test_base.py | 5 +++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/python/cuml/cuml/__init__.py b/python/cuml/cuml/__init__.py index 3fbd69e9be..cd570ae5b6 100644 --- a/python/cuml/cuml/__init__.py +++ b/python/cuml/cuml/__init__.py @@ -14,7 +14,6 @@ del libcuml import cupy -from pylibraft.common import Handle from rmm.allocators.cupy import rmm_cupy_allocator import cuml.feature_extraction @@ -87,6 +86,18 @@ def __getattr__(name): except AttributeError: _global_settings_data.settings = GlobalSettings() return _global_settings_data.settings + elif name == "Handle": + import warnings + + from pylibraft.common import Handle + + warnings.warn( + "cuml.Handle was deprecated in 26.02 and will be removed in 26.04. " + "There is no need to manually specify a `handle`, cuml now manages " + "this resource for you automatically.", + FutureWarning, + ) + return Handle raise AttributeError(f"module {__name__} has no attribute {name}") diff --git a/python/cuml/tests/test_base.py b/python/cuml/tests/test_base.py index 8b43943c33..0b90cd10bc 100644 --- a/python/cuml/tests/test_base.py +++ b/python/cuml/tests/test_base.py @@ -329,6 +329,11 @@ def create(handle=None): assert "n_streams" in str(rec[0].message) +def test_cuml_handle_deprecated(): + with pytest.warns(FutureWarning, match="cuml.Handle"): + assert cuml.Handle is pylibraft.common.handle.Handle + + def test_get_handle(): # Threadlocal is cached assert get_handle() is get_handle() From 8a6684d8f25db5880fd41023a0d03b7dd6d809e9 Mon Sep 17 00:00:00 2001 From: Jim Crist-Harif Date: Thu, 18 Dec 2025 20:54:50 -0600 Subject: [PATCH 18/21] Fixup test_class_enumerator --- python/cuml/tests/test_class_enumerator.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/python/cuml/tests/test_class_enumerator.py b/python/cuml/tests/test_class_enumerator.py index 8bec6615b9..a68b705607 100644 --- a/python/cuml/tests/test_class_enumerator.py +++ b/python/cuml/tests/test_class_enumerator.py @@ -56,21 +56,18 @@ def test_class_enumerator_actual_module(): cuml.MBSGDRegressor, ], custom_constructors={ - "LogisticRegression": lambda: cuml.LogisticRegression(handle=1) + "LogisticRegression": lambda: cuml.LogisticRegression(C=5.5) }, ) models = module.get_models() ref = { "ElasticNet": cuml.ElasticNet, "Lasso": cuml.Lasso, - "LogisticRegression": lambda: cuml.LogisticRegression(handle=1), + "LogisticRegression": lambda: cuml.LogisticRegression(C=5.5), "Ridge": cuml.Ridge, } - assert ( - models["LogisticRegression"]().handle - == ref["LogisticRegression"]().handle - ) + assert models["LogisticRegression"]().C == ref["LogisticRegression"]().C models.pop("LogisticRegression") ref.pop("LogisticRegression") assert models == ref From 26a31776c68bc871cf564167db90a1ecdf9886c5 Mon Sep 17 00:00:00 2001 From: Jim Crist-Harif Date: Thu, 18 Dec 2025 21:26:07 -0600 Subject: [PATCH 19/21] Fixup metrics docs These were using the wrong directive, leading to a parsing error. --- docs/source/api.rst | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/docs/source/api.rst b/docs/source/api.rst index 37d68f21b2..8dd1bca6b7 100644 --- a/docs/source/api.rst +++ b/docs/source/api.rst @@ -215,29 +215,24 @@ Metrics (regression, classification, and distance) Metrics (clustering and manifold learning) ------------------------------------------ - .. automodule:: cuml.metrics.trustworthiness - :members: - .. automodule:: cuml.metrics.cluster.adjusted_rand_index - :members: + .. autofunction:: cuml.metrics.trustworthiness - .. automodule:: cuml.metrics.cluster.entropy - :members: + .. autofunction:: cuml.metrics.cluster.adjusted_rand_score - .. automodule:: cuml.metrics.cluster.homogeneity_score - :members: + .. autofunction:: cuml.metrics.cluster.entropy - .. automodule:: cuml.metrics.cluster.silhouette_score - :members: + .. autofunction:: cuml.metrics.cluster.homogeneity_score - .. automodule:: cuml.metrics.cluster.completeness_score - :members: + .. autofunction:: cuml.metrics.cluster.silhouette_score - .. automodule:: cuml.metrics.cluster.mutual_info_score - :members: + .. autofunction:: cuml.metrics.cluster.silhouette_samples - .. automodule:: cuml.metrics.cluster.v_measure_score - :members: + .. autofunction:: cuml.metrics.cluster.completeness_score + + .. autofunction:: cuml.metrics.cluster.mutual_info_score + + .. autofunction:: cuml.metrics.cluster.v_measure_score Benchmarking ------------ From b28eae853f3f5b489f1de92ddea59ee9fbfe0b31 Mon Sep 17 00:00:00 2001 From: Jim Crist-Harif Date: Thu, 18 Dec 2025 22:52:47 -0600 Subject: [PATCH 20/21] Don't deprecate `handle` on `*MG` classes --- python/cuml/cuml/internals/base.py | 3 ++- python/cuml/tests/test_base.py | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/python/cuml/cuml/internals/base.py b/python/cuml/cuml/internals/base.py index bb4859cfca..3538671507 100644 --- a/python/cuml/cuml/internals/base.py +++ b/python/cuml/cuml/internals/base.py @@ -26,7 +26,8 @@ class DeprecatedHandleDescriptor: """A descriptor to ease deprecating the `handle` parameter.""" def __set__(self, obj, value): - if value is not None: + # Only warn if set to non-None on *non-multiGPU classes* + if value is not None and not type(obj).__name__.endswith("MG"): params = obj._get_param_names() if isinstance(obj, Base) else [] if "n_streams" in params: suffix = ( diff --git a/python/cuml/tests/test_base.py b/python/cuml/tests/test_base.py index 0b90cd10bc..c0708c365e 100644 --- a/python/cuml/tests/test_base.py +++ b/python/cuml/tests/test_base.py @@ -294,6 +294,7 @@ def test_common_signatures(cls, method): for cls in all_base_children.values() if not ( "Base" in cls.__name__ + or cls.__name__.endswith("MG") or cls.__module__.startswith("cuml.tsa") or cls.__name__ in ["ColumnTransformer"] ) From f3d10c1caaef7228749ecee88179b27e630c4492 Mon Sep 17 00:00:00 2001 From: Jim Crist-Harif Date: Fri, 19 Dec 2025 16:44:37 -0600 Subject: [PATCH 21/21] Respond to feedback --- .../cuml/ensemble/randomforest_common.pyx | 23 +++++++++++-------- python/cuml/cuml/internals/base.py | 1 + python/cuml/tests/test_random_forest.py | 19 +++++++++++++++ 3 files changed, 33 insertions(+), 10 deletions(-) diff --git a/python/cuml/cuml/ensemble/randomforest_common.pyx b/python/cuml/cuml/ensemble/randomforest_common.pyx index 5268eaf165..e9f437011f 100644 --- a/python/cuml/cuml/ensemble/randomforest_common.pyx +++ b/python/cuml/cuml/ensemble/randomforest_common.pyx @@ -397,16 +397,19 @@ class BaseRandomForestModel(Base, InteropMixin): A Forest Inference model which can be used to perform inferencing on the random forest model. """ - return ForestInference( - handle=self.handle, - verbose=self.verbose, - output_type=self.output_type, - treelite_model=self._treelite_model_bytes, - is_classifier=(self._estimator_type == "classifier"), - layout=layout, - default_chunk_size=default_chunk_size, - align_bytes=align_bytes, - ) + with warnings.catch_warnings(): + # Ignore potential handle deprecation warning + warnings.simplefilter("ignore", category=FutureWarning) + return ForestInference( + handle=self.handle, + verbose=self.verbose, + output_type=self.output_type, + treelite_model=self._treelite_model_bytes, + is_classifier=(self._estimator_type == "classifier"), + layout=layout, + default_chunk_size=default_chunk_size, + align_bytes=align_bytes, + ) def _fit_forest(self, X, y): cdef bool is_classifier = self._estimator_type == "classifier" diff --git a/python/cuml/cuml/internals/base.py b/python/cuml/cuml/internals/base.py index 3538671507..66ec0db6fa 100644 --- a/python/cuml/cuml/internals/base.py +++ b/python/cuml/cuml/internals/base.py @@ -93,6 +93,7 @@ def get_handle(*, handle=None, model=None, n_streams=0, device_ids=None): return _THREAD_STATE.handle elif device_ids is not None: if n_streams != 0: + # DeviceResourcesSNMG doesn't support `n_streams` at this time raise ValueError( "Cannot specify both `device_ids` and `n_streams`" ) diff --git a/python/cuml/tests/test_random_forest.py b/python/cuml/tests/test_random_forest.py index 5d690f7678..22f0cc713e 100644 --- a/python/cuml/tests/test_random_forest.py +++ b/python/cuml/tests/test_random_forest.py @@ -183,6 +183,25 @@ def special_reg(request): return X, y +def test_as_fil_doesnt_warn_handle_deprecated(): + X, y = make_classification() + with pytest.warns(FutureWarning): + handle = cuml.Handle(n_streams=4) + model = cuml.RandomForestClassifier( + handle=handle, n_streams=4, n_bins=32 + ) + + model.fit(X, y) + + # Test that no warning raised + with warnings.catch_warnings(): + warnings.simplefilter("error") + fil = model.as_fil() + + # handle was forwarded + assert fil.handle is handle + + def test_default_parameters(): reg_params = curfr().get_params() clf_params = curfc().get_params()