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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions dependencies.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -664,7 +664,7 @@ dependencies:
dependencies: "oldest"
cuda: "12.*"
packages:
- cuda-bindings==12.9.2
- cuda-bindings==12.9.3
- matrix:
dependencies: "oldest"
cuda: "13.*"
Expand Down Expand Up @@ -712,7 +712,7 @@ dependencies:
matrices:
- matrix: {dependencies: "latest"}
packages:
- scikit-learn==1.9.0
- scikit-learn==1.9.1
- matrix: {dependencies: "intermediate"}
packages:
- scikit-learn==1.8.0
Expand Down
2 changes: 1 addition & 1 deletion docs/source/cuml-accel/compatibility.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ General Behavior
----------------

**Compatibility**
The accelerator is tested with ``scikit-learn`` versions 1.6 through 1.9,
The accelerator is tested with ``scikit-learn`` versions 1.6 through 1.9.1,
``umap-learn`` versions 0.5.7 through 0.5.12, and ``hdbscan`` versions 0.8.39
through 0.8.44. When ``cuml.accel`` detects a version outside these ranges,
it issues a runtime warning and continues. The untested version will likely
Expand Down
21 changes: 11 additions & 10 deletions python/cuml/cuml/_thirdparty/sklearn/preprocessing/_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -2463,17 +2463,17 @@ def _sparse_fit(self, X, random_state):
column_nnz_data = X.data[X.indptr[feature_idx]:
X.indptr[feature_idx + 1]]
if len(column_nnz_data) > self.subsample:
column_subsample = (self.subsample * len(column_nnz_data) //
n_samples)
if self.ignore_implicit_zeros:
column_data = np.zeros(shape=column_subsample,
dtype=X.dtype)
else:
column_data = np.zeros(shape=self.subsample, dtype=X.dtype)
column_data = np.zeros(shape=self.subsample, dtype=X.dtype)
column_subsample = (
self.subsample
if self.ignore_implicit_zeros
else self.subsample * len(column_nnz_data) // n_samples
)
column_data[:column_subsample] = np.array(
random_state.choice(column_nnz_data.get(),
size=column_subsample,
replace=False))
random_state.choice(
column_nnz_data.get(), size=column_subsample, replace=False
)
)
else:
if self.ignore_implicit_zeros:
column_data = np.zeros(shape=len(column_nnz_data),
Expand All @@ -2491,6 +2491,7 @@ def _sparse_fit(self, X, random_state):
cpu_np.nanpercentile(np.asnumpy(column_data),
np.asnumpy(references)))
self.quantiles_ = cpu_np.transpose(np.asnumpy(self.quantiles_))

# due to floating-point precision error in `np.nanpercentile`,
# make sure the quantiles are monotonically increasing
# Upstream issue in numpy:
Expand Down
4 changes: 2 additions & 2 deletions python/cuml/cuml/accel/core.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#
# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION.
# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
from __future__ import annotations
Expand Down Expand Up @@ -119,7 +119,7 @@ def __call__(self) -> bool:


_CONSTRAINTS = {
"sklearn": CheckConstraint("scikit-learn>=1.6.0,<=1.9.0"),
"sklearn": CheckConstraint("scikit-learn>=1.6.0,<=1.9.1"),
"hdbscan": CheckConstraint("hdbscan>=0.8.39,<=0.8.44"),
"umap": CheckConstraint("umap-learn>=0.5.7,<=0.5.12"),
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1195,3 +1195,8 @@
- "sklearn.linear_model.tests.test_logistic::test_liblinear_dual_random_state[42]"
- "sklearn.linear_model.tests.test_logistic::test_liblinear_with_large_values"
- "sklearn.svm.tests.test_svm::test_liblinear_set_coef[42]"
- reason: sklearn >= 1.9.1 test failures
condition: scikit-learn>=1.9.1
tests:
- "sklearn.neighbors.tests.test_neighbors::test_radius_neighbors_parallel_array_radius[ball_tree]"
- "sklearn.neighbors.tests.test_neighbors::test_radius_neighbors_parallel_array_radius[kd_tree]"
54 changes: 50 additions & 4 deletions python/cuml/tests/test_preprocessing.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@
sparse_nan_filled_positive,
)

SKLEARN_VERSION = Version(sklearn.__version__)


@pytest.mark.parametrize("feature_range", [(0, 1), (0.1, 0.8)])
def test_minmax_scaler(
Expand Down Expand Up @@ -821,9 +823,7 @@ def test_kbinsdiscretizer(
assert type(r_X) is type(t_X)

sklearn_kwargs = {}
if strategy == "quantile" and Version(sklearn.__version__) >= Version(
"1.7"
):
if strategy == "quantile" and SKLEARN_VERSION >= Version("1.7"):
# cuML uses linear percentile interpolation. Scikit-learn exposed the
# method in 1.7 and changed its default in 1.9.
sklearn_kwargs["quantile_method"] = "linear"
Expand Down Expand Up @@ -1061,7 +1061,23 @@ def test_quantile_transformer(

@pytest.mark.parametrize("n_quantiles", [30, 100])
@pytest.mark.parametrize("output_distribution", ["uniform", "normal"])
@pytest.mark.parametrize("ignore_implicit_zeros", [False, True])
@pytest.mark.parametrize(
"ignore_implicit_zeros",
[
False,
pytest.param(
True,
marks=pytest.mark.xfail(
SKLEARN_VERSION < Version("1.9.1"),
reason=(
"sklearn bug in sparse quantiles with ignore_implicit_zeros "
"in sklearn <= 1.9.0"
),
strict=True,
),
),
],
)
@pytest.mark.parametrize("subsample", [100])
def test_quantile_transformer_sparse(
failure_logger,
Expand Down Expand Up @@ -1116,6 +1132,36 @@ def test_quantile_transformer_sparse(
assert_allclose(r_X, sk_r_X)


def test_quantile_transformer_sparse_subsampling_ignore_implicit_zeros():
subsample = 500
kws = dict(subsample=subsample, n_quantiles=50, random_state=42)

# A very sparse X matrix with two similar columns.
# One with nnz `subsample - 1`, the other with nnz `subsample + 1`
row = cp.arange(subsample * 2)
col = cp.asarray([0, 1]).repeat([subsample - 1, subsample + 1])
data = cp.concatenate(
(
cp.linspace(1, 2, num=subsample - 1),
cp.linspace(1, 2, num=subsample + 1),
)
)
X = cpx.scipy.sparse.csc_array(
(data, (row, col)),
shape=(2 * subsample**2, 2),
)

qt = cuQuantileTransformer(ignore_implicit_zeros=True, **kws).fit(X)
quantiles = qt.quantiles_.T
assert (qt.quantiles_ > 0).all()
assert not cp.all(quantiles[1] == quantiles[1][0])

# if ignore_implicit_zeros=False, quantiles are mostly zeros
qt = cuQuantileTransformer(ignore_implicit_zeros=False, **kws).fit(X)
quantiles = qt.fit(X).quantiles_
assert cp.isclose(quantiles, 0).mean() > 0.9


@pytest.mark.filterwarnings(
"ignore:'ignore_implicit_zeros' takes effect only with sparse matrix.*:UserWarning"
)
Expand Down
Loading