use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
use scirs2_core::ndarray::{Array1, Array2};
use scirs2_core::random::essentials::Normal;
use scirs2_core::random::{seeded_rng, Distribution};
use sklears_python::{train_test_split_core, PyDBSCAN, PyKFold, PyKMeans};
use std::hint::black_box;
fn generate_blob_data(
n_samples: usize,
n_features: usize,
n_clusters: usize,
seed: u64,
) -> Array2<f64> {
let mut rng = seeded_rng(seed);
let normal = Normal::new(0.0, 1.0).expect("operation should succeed");
let mut data = Array2::<f64>::zeros((n_samples, n_features));
let samples_per_cluster = n_samples / n_clusters;
for cluster_id in 0..n_clusters {
let start = cluster_id * samples_per_cluster;
let end = if cluster_id == n_clusters - 1 {
n_samples
} else {
(cluster_id + 1) * samples_per_cluster
};
let center = cluster_id as f64 * 20.0;
for i in start..end {
for j in 0..n_features {
data[[i, j]] = center + normal.sample(&mut rng);
}
}
}
data
}
fn generate_indexed_dataset(
n_samples: usize,
n_features: usize,
seed: u64,
) -> (Array2<f64>, Array1<f64>) {
let mut rng = seeded_rng(seed);
let normal = Normal::new(0.0, 1.0).expect("operation should succeed");
let x_data: Vec<f64> = (0..n_samples * n_features)
.map(|_| normal.sample(&mut rng))
.collect();
let x =
Array2::from_shape_vec((n_samples, n_features), x_data).expect("shape matches data length");
let y = Array1::from_vec((0..n_samples).map(|i| i as f64).collect());
(x, y)
}
fn benchmark_kmeans_fit_predict(c: &mut Criterion) {
let mut group = c.benchmark_group("kmeans_fit_predict");
group.sample_size(10);
for size in [100, 1000, 5000].iter() {
let x = generate_blob_data(*size, 4, 4, 42);
group.throughput(Throughput::Elements(*size as u64));
group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, _| {
b.iter(|| {
let mut model = PyKMeans::new(4, 300, 1e-4, Some(42));
model.fit_core(black_box(&x)).expect("fit should succeed");
black_box(model.predict_core(&x).expect("predict should succeed"))
});
});
}
group.finish();
}
fn benchmark_dbscan_fit_predict(c: &mut Criterion) {
let mut group = c.benchmark_group("dbscan_fit_predict");
group.sample_size(10);
for size in [100, 1000, 5000].iter() {
let x = generate_blob_data(*size, 4, 4, 7);
group.throughput(Throughput::Elements(*size as u64));
group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, _| {
b.iter(|| {
let mut model = PyDBSCAN::new(3.0, 5);
black_box(
model
.fit_predict_core(black_box(&x))
.expect("fit_predict should succeed"),
)
});
});
}
group.finish();
}
fn benchmark_train_test_split(c: &mut Criterion) {
let mut group = c.benchmark_group("train_test_split");
for size in [100, 1000, 5000].iter() {
let (x, y) = generate_indexed_dataset(*size, 10, 99);
group.throughput(Throughput::Elements(*size as u64));
group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, _| {
b.iter(|| {
black_box(
train_test_split_core(black_box(&x), &y, Some(0.25), Some(42))
.expect("split should succeed"),
)
});
});
}
group.finish();
}
fn benchmark_kfold_split(c: &mut Criterion) {
let mut group = c.benchmark_group("kfold_split");
for size in [100, 1000, 5000].iter() {
let kfold = PyKFold::new(5, false, None).expect("n_splits=5 is valid");
group.throughput(Throughput::Elements(*size as u64));
group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, _| {
b.iter(|| {
black_box(
kfold
.split_core(black_box(*size))
.expect("split should succeed"),
)
});
});
}
group.finish();
}
criterion_group!(
core_helpers,
benchmark_kmeans_fit_predict,
benchmark_dbscan_fit_predict,
benchmark_train_test_split,
benchmark_kfold_split,
);
criterion_main!(core_helpers);