From 70ec4287bf88181a6b1644b602eaa6e94e8ccf01 Mon Sep 17 00:00:00 2001 From: Alexandr-Solovev Date: Fri, 6 Nov 2026 13:29:08 -0800 Subject: [PATCH 1/3] fix(dbscan): compute the clustering metrics from the converted labels on GPU The DBSCAN quality metrics passed the dpnp ground-truth labels to np.unique, which refuses the implicit conversion, so every sklearnex GPU DBSCAN case failed after the fit. Use the converted copy, as the other metrics do. Co-Authored-By: Claude Opus 5.5 Signed-off-by: Alexandr-Solovev --- sklbench/benchmarks/sklearn_estimator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sklbench/benchmarks/sklearn_estimator.py b/sklbench/benchmarks/sklearn_estimator.py index 8c511ce3..dd9eb06a 100644 --- a/sklbench/benchmarks/sklearn_estimator.py +++ b/sklbench/benchmarks/sklearn_estimator.py @@ -225,7 +225,7 @@ def get_subset_metrics_of_estimator( ) } ) - if len(np.unique(y)) < 128: + if len(np.unique(y_compat)) < 128: metrics.update( { "homogeneity": ( From 837066296053d753f5c26c64b79f0be12a23a592 Mon Sep 17 00:00:00 2001 From: Alexandr-Solovev Date: Fri, 6 Nov 2026 13:29:08 -0800 Subject: [PATCH 2/3] feat(dbscan): take the eps quantile in the estimator's metric and add a metrics experiment distances_quantile always used euclidean distances, so eps had the wrong scale for manhattan, chebyshev, cosine and minkowski. It now uses the metric (and p) from the estimator parameters; euclidean cases get the same value as before. configs/experiments/dbscan_metrics.json runs DBSCAN with the five metrics that scikit-learn-intelex runs in oneDAL on blobs, cifar and covtype, against scikit-learn's brute and auto algorithms. Co-Authored-By: Claude Opus 5.5 Signed-off-by: Alexandr-Solovev --- configs/experiments/dbscan_metrics.json | 78 +++++++++++++++++++++++++ sklbench/utils/special_params.py | 15 ++++- 2 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 configs/experiments/dbscan_metrics.json diff --git a/configs/experiments/dbscan_metrics.json b/configs/experiments/dbscan_metrics.json new file mode 100644 index 00000000..5ea6150e --- /dev/null +++ b/configs/experiments/dbscan_metrics.json @@ -0,0 +1,78 @@ +{ + "INCLUDE": ["../common/sklearn.json"], + "PARAMETERS_SETS": { + "dbscan metrics": { + "algorithm": { + "estimator": "DBSCAN", + "estimator_params": [ + { "metric": "euclidean" }, + { "metric": "manhattan" }, + { "metric": "chebyshev" }, + { "metric": "cosine" }, + { "metric": "minkowski", "p": 3 } + ] + } + }, + "dbscan eps and jobs": { + "algorithm": { + "estimator_params": { + "eps": "[SPECIAL_VALUE]distances_quantile:0.01", + "min_samples": 5, + "n_jobs": "[SPECIAL_VALUE]physical_cpus" + } + }, + "bench": { "n_runs": 5, "time_limit": 900 } + }, + "sklearn brute and tree": [ + { "algorithm": { "library": "sklearn", "device": "cpu", "estimator_params": { "algorithm": "brute" } } }, + { "algorithm": { "library": "sklearn", "device": "cpu", "estimator_params": { "algorithm": "auto" } } } + ], + "sklearnex cpu": { + "algorithm": { "library": "sklearnex", "device": "cpu", "estimator_params": { "algorithm": "brute" } } + }, + "sklearnex gpu": { + "algorithm": { + "library": "sklearnex", + "device": "gpu", + "estimator_params": { "algorithm": "brute" }, + "sklearn_context": { "array_api_dispatch": true } + }, + "data": { "format": "dpnp", "order": "C" } + }, + "dbscan datasets": { + "data": [ + { + "source": "make_blobs", + "generation_kwargs": [ + { "centers": 20, "n_samples": 50000, "n_features": 8, "cluster_std": 1.0 }, + { "centers": 20, "n_samples": 200000, "n_features": 8, "cluster_std": 1.0 }, + { "centers": 20, "n_samples": 100000, "n_features": 32, "cluster_std": 1.0 }, + { "centers": 100, "n_samples": 500000, "n_features": 2, "cluster_std": 1.0 } + ], + "split_kwargs": { "ignore": true } + }, + { + "dataset": "cifar", + "split_kwargs": { "ignore": true }, + "preprocessing_kwargs": { "normalize": "mean" } + }, + { + "dataset": "covtype", + "split_kwargs": { "train_size": 100000, "test_size": null }, + "preprocessing_kwargs": { "normalize": "standard" } + } + ] + } + }, + "TEMPLATES": { + "sklearn dbscan": { + "SETS": ["sklearn brute and tree", "dbscan metrics", "dbscan eps and jobs", "dbscan datasets"] + }, + "sklearnex cpu dbscan": { + "SETS": ["sklearnex cpu", "dbscan metrics", "dbscan eps and jobs", "dbscan datasets"] + }, + "sklearnex gpu dbscan": { + "SETS": ["sklearnex gpu", "dbscan metrics", "dbscan eps and jobs", "dbscan datasets"] + } + } +} diff --git a/sklbench/utils/special_params.py b/sklbench/utils/special_params.py index 80812012..2c5a994f 100644 --- a/sklbench/utils/special_params.py +++ b/sklbench/utils/special_params.py @@ -21,7 +21,7 @@ import numpy as np import pandas as pd from psutil import cpu_count -from sklearn.metrics import euclidean_distances +from sklearn.metrics import pairwise_distances from ..datasets import dataset_loading_functions from .bench_case import get_bench_case_value, set_bench_case_value @@ -288,7 +288,18 @@ def assign_case_special_values_on_run( # conversion to lower precision is required # to produce same distances quantile for different dtypes of x x_sample = x_sample.astype("float32") - dist = np.tril(euclidean_distances(x_sample, x_sample)).reshape(-1) + # the quantile is taken in the estimator's own metric, so eps has the right scale + metric = get_bench_case_value( + bench_case, "algorithm:estimator_params:metric", "euclidean" + ) + metric_kwargs = {} + if metric == "minkowski": + metric_kwargs["p"] = get_bench_case_value( + bench_case, "algorithm:estimator_params:p", 2 + ) + dist = np.tril( + pairwise_distances(x_sample, x_sample, metric=metric, **metric_kwargs) + ).reshape(-1) dist = dist[dist != 0] quantile = float(np.quantile(dist, quantile)) set_bench_case_value(bench_case, "algorithm:estimator_params:eps", quantile) From 65ed4c3134633f671c40cee86cd400cffcaa9354 Mon Sep 17 00:00:00 2001 From: Alexandr-Solovev Date: Wed, 11 Nov 2026 01:59:15 -0800 Subject: [PATCH 3/3] feat(dbscan): add a 50000 x 256 dataset to the metrics experiment The experiment had at most 54 columns apart from cifar; the new blobs cover wide data with fewer rows. Co-Authored-By: Claude Opus 5.5 Signed-off-by: Alexandr-Solovev --- configs/experiments/dbscan_metrics.json | 1 + 1 file changed, 1 insertion(+) diff --git a/configs/experiments/dbscan_metrics.json b/configs/experiments/dbscan_metrics.json index 5ea6150e..aeba8ea4 100644 --- a/configs/experiments/dbscan_metrics.json +++ b/configs/experiments/dbscan_metrics.json @@ -47,6 +47,7 @@ { "centers": 20, "n_samples": 50000, "n_features": 8, "cluster_std": 1.0 }, { "centers": 20, "n_samples": 200000, "n_features": 8, "cluster_std": 1.0 }, { "centers": 20, "n_samples": 100000, "n_features": 32, "cluster_std": 1.0 }, + { "centers": 20, "n_samples": 50000, "n_features": 256, "cluster_std": 1.0 }, { "centers": 100, "n_samples": 500000, "n_features": 2, "cluster_std": 1.0 } ], "split_kwargs": { "ignore": true }