From ae7b992cf78b28bb23c45e05bc6717a411e3f6a7 Mon Sep 17 00:00:00 2001 From: Jim Crist-Harif Date: Tue, 26 May 2026 12:07:12 -0500 Subject: [PATCH 1/4] Note TSNE failure due to `n_components=1` erroring --- python/cuml/tests/test_sklearn_compatibility.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/cuml/tests/test_sklearn_compatibility.py b/python/cuml/tests/test_sklearn_compatibility.py index 017234f856..051d04dcb8 100644 --- a/python/cuml/tests/test_sklearn_compatibility.py +++ b/python/cuml/tests/test_sklearn_compatibility.py @@ -182,7 +182,7 @@ }, TSNE: { "check_estimator_tags_renamed": "No support for modern tags infrastructure", - "check_dont_overwrite_parameters": "TSNE overwrites parameters during fit", + "check_dont_overwrite_parameters": "TSNE only supports n_components = 2", "check_pipeline_consistency": "TSNE results are not deterministic", "check_methods_sample_order_invariance": "TSNE results depend on sample order", "check_methods_subset_invariance": "TSNE results depend on data subset", From 402c67d4ceb3d9099c8150d8585780a64410d2ab Mon Sep 17 00:00:00 2001 From: Jim Crist-Harif Date: Tue, 26 May 2026 12:33:46 -0500 Subject: [PATCH 2/4] Use explicit kwargs in all estimators --- .../cuml/ensemble/randomforest_common.pyx | 1 - .../cuml/ensemble/randomforestclassifier.py | 30 +++++++++++++++++-- .../cuml/ensemble/randomforestregressor.py | 28 +++++++++++++++-- .../cuml/neighbors/kneighbors_classifier.pyx | 20 +++++++++++-- .../cuml/neighbors/kneighbors_regressor.pyx | 20 +++++++++++-- .../cuml/cuml/neighbors/nearest_neighbors.pyx | 2 +- .../cuml/tests/test_sklearn_compatibility.py | 4 --- 7 files changed, 91 insertions(+), 14 deletions(-) diff --git a/python/cuml/cuml/ensemble/randomforest_common.pyx b/python/cuml/cuml/ensemble/randomforest_common.pyx index 8fd9d2ad7d..a28c066ed4 100644 --- a/python/cuml/cuml/ensemble/randomforest_common.pyx +++ b/python/cuml/cuml/ensemble/randomforest_common.pyx @@ -322,7 +322,6 @@ class BaseRandomForestModel(Base, InteropMixin): min_impurity_decrease=0.0, max_batch_size=4096, random_state=None, - criterion=None, n_streams=4, oob_score=False, verbose=False, diff --git a/python/cuml/cuml/ensemble/randomforestclassifier.py b/python/cuml/cuml/ensemble/randomforestclassifier.py index 299b5941b7..a251b04865 100644 --- a/python/cuml/cuml/ensemble/randomforestclassifier.py +++ b/python/cuml/cuml/ensemble/randomforestclassifier.py @@ -190,16 +190,42 @@ def _attrs_to_cpu(self, model): def __init__( self, *, + n_estimators=100, split_criterion="gini", + bootstrap=True, + max_samples=1.0, + max_depth="deprecated", + max_leaves=-1, + max_features="sqrt", + n_bins=128, + min_samples_leaf=1, + min_samples_split=2, + min_impurity_decrease=0.0, + max_batch_size=4096, + random_state=None, + n_streams=4, + oob_score=False, verbose=False, output_type=None, - **kwargs, ): super().__init__( split_criterion=split_criterion, + n_estimators=n_estimators, + bootstrap=bootstrap, + max_samples=max_samples, + max_depth=max_depth, + max_leaves=max_leaves, + max_features=max_features, + n_bins=n_bins, + min_samples_leaf=min_samples_leaf, + min_samples_split=min_samples_split, + min_impurity_decrease=min_impurity_decrease, + max_batch_size=max_batch_size, + random_state=random_state, + n_streams=n_streams, + oob_score=oob_score, verbose=verbose, output_type=output_type, - **kwargs, ) @nvtx.annotate( diff --git a/python/cuml/cuml/ensemble/randomforestregressor.py b/python/cuml/cuml/ensemble/randomforestregressor.py index 9139c840a7..556e6d9cc6 100644 --- a/python/cuml/cuml/ensemble/randomforestregressor.py +++ b/python/cuml/cuml/ensemble/randomforestregressor.py @@ -160,18 +160,42 @@ class RandomForestRegressor(BaseRandomForestModel, RegressorMixin): def __init__( self, *, + n_estimators=100, split_criterion="mse", + bootstrap=True, + max_samples=1.0, + max_depth="deprecated", + max_leaves=-1, max_features=1.0, + n_bins=128, + min_samples_leaf=1, + min_samples_split=2, + min_impurity_decrease=0.0, + max_batch_size=4096, + random_state=None, + n_streams=4, + oob_score=False, verbose=False, output_type=None, - **kwargs, ): super().__init__( + n_estimators=n_estimators, split_criterion=split_criterion, + bootstrap=bootstrap, + max_samples=max_samples, + max_depth=max_depth, + max_leaves=max_leaves, max_features=max_features, + n_bins=n_bins, + min_samples_leaf=min_samples_leaf, + min_samples_split=min_samples_split, + min_impurity_decrease=min_impurity_decrease, + max_batch_size=max_batch_size, + random_state=random_state, + n_streams=n_streams, + oob_score=oob_score, verbose=verbose, output_type=output_type, - **kwargs, ) @nvtx.annotate( diff --git a/python/cuml/cuml/neighbors/kneighbors_classifier.pyx b/python/cuml/cuml/neighbors/kneighbors_classifier.pyx index 2f528fa334..73a087c5e0 100644 --- a/python/cuml/cuml/neighbors/kneighbors_classifier.pyx +++ b/python/cuml/cuml/neighbors/kneighbors_classifier.pyx @@ -154,12 +154,28 @@ class KNeighborsClassifier(ClassifierMixin, FMajorInputTagMixin, NeighborsBase): def __init__( self, *, + n_neighbors=5, + algorithm="auto", + metric="euclidean", weights="uniform", + p=2, + algo_params=None, + metric_params=None, + n_jobs=None, # Ignored, here for sklearn API compatibility verbose=False, output_type=None, - **kwargs, ): - super().__init__(verbose=verbose, output_type=output_type, **kwargs) + super().__init__( + n_neighbors=n_neighbors, + algorithm=algorithm, + metric=metric, + p=p, + algo_params=algo_params, + metric_params=metric_params, + n_jobs=n_jobs, + verbose=verbose, + output_type=output_type, + ) self.weights = weights @generate_docstring(convert_dtype_cast='np.float32') diff --git a/python/cuml/cuml/neighbors/kneighbors_regressor.pyx b/python/cuml/cuml/neighbors/kneighbors_regressor.pyx index 648f6b8fa4..c9e73d2d2e 100644 --- a/python/cuml/cuml/neighbors/kneighbors_regressor.pyx +++ b/python/cuml/cuml/neighbors/kneighbors_regressor.pyx @@ -156,12 +156,28 @@ class KNeighborsRegressor(RegressorMixin, FMajorInputTagMixin, NeighborsBase): def __init__( self, *, + n_neighbors=5, + algorithm="auto", + metric="euclidean", weights="uniform", + p=2, + algo_params=None, + metric_params=None, + n_jobs=None, # Ignored, here for sklearn API compatibility verbose=False, output_type=None, - **kwargs, ): - super().__init__(verbose=verbose, output_type=output_type, **kwargs) + super().__init__( + n_neighbors=n_neighbors, + algorithm=algorithm, + metric=metric, + p=p, + algo_params=algo_params, + metric_params=metric_params, + n_jobs=n_jobs, + verbose=verbose, + output_type=output_type, + ) self.weights = weights @generate_docstring(convert_dtype_cast='np.float32') diff --git a/python/cuml/cuml/neighbors/nearest_neighbors.pyx b/python/cuml/cuml/neighbors/nearest_neighbors.pyx index 43af59d66b..229c18fc79 100644 --- a/python/cuml/cuml/neighbors/nearest_neighbors.pyx +++ b/python/cuml/cuml/neighbors/nearest_neighbors.pyx @@ -559,13 +559,13 @@ class NeighborsBase(Base, InteropMixin, CMajorInputTagMixin, SparseInputTagMixin self, *, n_neighbors=5, - verbose=False, algorithm="auto", metric="euclidean", p=2, algo_params=None, metric_params=None, n_jobs=None, # Ignored, here for sklearn API compatibility + verbose=False, output_type=None, ): super().__init__(verbose=verbose, output_type=output_type) diff --git a/python/cuml/tests/test_sklearn_compatibility.py b/python/cuml/tests/test_sklearn_compatibility.py index 051d04dcb8..9a17138727 100644 --- a/python/cuml/tests/test_sklearn_compatibility.py +++ b/python/cuml/tests/test_sklearn_compatibility.py @@ -118,24 +118,20 @@ }, RandomForestRegressor: { "check_estimator_tags_renamed": "No support for modern tags infrastructure", - "check_do_not_raise_errors_in_init_or_set_params": "RandomForestRegressor raises errors in init or set_params", "check_regressor_data_not_an_array": "RandomForestRegressor does not handle non-array data", "check_dict_unchanged": "RandomForestRegressor modifies input dictionaries", }, RandomForestClassifier: { "check_estimator_tags_renamed": "No support for modern tags infrastructure", - "check_do_not_raise_errors_in_init_or_set_params": "RandomForestClassifier raises errors in init or set_params", "check_classifier_data_not_an_array": "RandomForestClassifier does not handle non-array data", "check_dict_unchanged": "RandomForestClassifier modifies input dictionaries", }, KNeighborsClassifier: { "check_estimator_tags_renamed": "No support for modern tags infrastructure", - "check_do_not_raise_errors_in_init_or_set_params": "KNeighborsClassifier raises errors in init or set_params", "check_classifier_data_not_an_array": "KNeighborsClassifier does not handle non-array data", }, KNeighborsRegressor: { "check_estimator_tags_renamed": "No support for modern tags infrastructure", - "check_do_not_raise_errors_in_init_or_set_params": "KNeighborsRegressor raises errors in init or set_params", "check_regressor_data_not_an_array": "KNeighborsRegressor does not handle non-array data", "check_supervised_y_2d": "KNeighborsRegressor does not handle 2D y", }, From 664cbc8e2e6286ab1f8912ced3d7d13ccdc146cd Mon Sep 17 00:00:00 2001 From: Jim Crist-Harif Date: Tue, 26 May 2026 12:37:54 -0500 Subject: [PATCH 3/4] Eagerly load nvforest model in `fit` --- python/cuml/cuml/ensemble/randomforest_common.pyx | 4 ++-- python/cuml/tests/test_sklearn_compatibility.py | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/python/cuml/cuml/ensemble/randomforest_common.pyx b/python/cuml/cuml/ensemble/randomforest_common.pyx index a28c066ed4..99f5e9142d 100644 --- a/python/cuml/cuml/ensemble/randomforest_common.pyx +++ b/python/cuml/cuml/ensemble/randomforest_common.pyx @@ -643,8 +643,8 @@ class BaseRandomForestModel(Base, InteropMixin): ) self.n_outputs_ = 1 self._treelite_model_bytes = (tl_bytes[:tl_bytes_len]) - # Ensure cached nvforest model is reset - self._nvforest_model = None + # Reload nvforest model + self._nvforest_model = self.as_nvforest() # Compute OOB score if requested if self.oob_score: diff --git a/python/cuml/tests/test_sklearn_compatibility.py b/python/cuml/tests/test_sklearn_compatibility.py index 9a17138727..351bdc1264 100644 --- a/python/cuml/tests/test_sklearn_compatibility.py +++ b/python/cuml/tests/test_sklearn_compatibility.py @@ -119,12 +119,10 @@ RandomForestRegressor: { "check_estimator_tags_renamed": "No support for modern tags infrastructure", "check_regressor_data_not_an_array": "RandomForestRegressor does not handle non-array data", - "check_dict_unchanged": "RandomForestRegressor modifies input dictionaries", }, RandomForestClassifier: { "check_estimator_tags_renamed": "No support for modern tags infrastructure", "check_classifier_data_not_an_array": "RandomForestClassifier does not handle non-array data", - "check_dict_unchanged": "RandomForestClassifier modifies input dictionaries", }, KNeighborsClassifier: { "check_estimator_tags_renamed": "No support for modern tags infrastructure", From 40f7e4050b5321aad073b3815fad5e54c5c6c63c Mon Sep 17 00:00:00 2001 From: Jim Crist-Harif Date: Tue, 26 May 2026 14:17:57 -0500 Subject: [PATCH 4/4] Docs fixups to appease the robot --- .../cuml/neighbors/kneighbors_classifier.pyx | 30 +++++++++++++++++++ .../cuml/neighbors/kneighbors_regressor.pyx | 30 +++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/python/cuml/cuml/neighbors/kneighbors_classifier.pyx b/python/cuml/cuml/neighbors/kneighbors_classifier.pyx index 73a087c5e0..e1c5a912fb 100644 --- a/python/cuml/cuml/neighbors/kneighbors_classifier.pyx +++ b/python/cuml/cuml/neighbors/kneighbors_classifier.pyx @@ -74,6 +74,36 @@ class KNeighborsClassifier(ClassifierMixin, FMajorInputTagMixin, NeighborsBase): - [callable] : a user-defined function which accepts an array of distances, and returns an array of the same shape containing the weights. + p : float (default=2) + Parameter for the Minkowski metric. When p = 1, this is equivalent to + manhattan distance (l1), and euclidean distance (l2) for p = 2. For + arbitrary p, minkowski distance (lp) is used. + algo_params : dict, optional (default=None) + Used to configure the nearest neighbor algorithm to be used. + If set to None, parameters will be generated automatically. + Parameters for algorithm ``'brute'`` when inputs are sparse: + + - batch_size_index : (int) number of rows in each batch of \ + index array + - batch_size_query : (int) number of rows in each batch of \ + query array + + Parameters for algorithm ``'ivfflat'``: + + - nlist: (int) number of cells to partition dataset into + - nprobe: (int) at query time, number of cells used for search + + Parameters for algorithm ``'ivfpq'``: + + - nlist: (int) number of cells to partition dataset into + - nprobe: (int) at query time, number of cells used for search + - M: (int) number of subquantizers + - n_bits: (int) bits allocated per subquantizer + - usePrecomputedTables : (bool) whether to use precomputed tables + metric_params : dict, optional (default = None) + Additional keyword arguments for the metric function. + n_jobs : int (default = None) + Ignored, here for scikit-learn API compatibility. 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 c9e73d2d2e..a24f786f21 100644 --- a/python/cuml/cuml/neighbors/kneighbors_regressor.pyx +++ b/python/cuml/cuml/neighbors/kneighbors_regressor.pyx @@ -76,6 +76,36 @@ class KNeighborsRegressor(RegressorMixin, FMajorInputTagMixin, NeighborsBase): - [callable] : a user-defined function which accepts an array of distances, and returns an array of the same shape containing the weights. + p : float (default=2) + Parameter for the Minkowski metric. When p = 1, this is equivalent to + manhattan distance (l1), and euclidean distance (l2) for p = 2. For + arbitrary p, minkowski distance (lp) is used. + algo_params : dict, optional (default=None) + Used to configure the nearest neighbor algorithm to be used. + If set to None, parameters will be generated automatically. + Parameters for algorithm ``'brute'`` when inputs are sparse: + + - batch_size_index : (int) number of rows in each batch of \ + index array + - batch_size_query : (int) number of rows in each batch of \ + query array + + Parameters for algorithm ``'ivfflat'``: + + - nlist: (int) number of cells to partition dataset into + - nprobe: (int) at query time, number of cells used for search + + Parameters for algorithm ``'ivfpq'``: + + - nlist: (int) number of cells to partition dataset into + - nprobe: (int) at query time, number of cells used for search + - M: (int) number of subquantizers + - n_bits: (int) bits allocated per subquantizer + - usePrecomputedTables : (bool) whether to use precomputed tables + metric_params : dict, optional (default = None) + Additional keyword arguments for the metric function. + n_jobs : int (default = None) + Ignored, here for scikit-learn API compatibility. 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.