COOLJAPAN
← All posts

sklears 0.2.0 Released — A Real CUDA GPU Foundation, Not Another Stub

sklears 0.2.0 ships sklears-core::gpu, a real oxicuda-backed CUDA foundation (GpuBackend, GpuArray, GpuMatrixOps) powering on-device GEMM, Cholesky/LU/QR/SVD solves, and HNSW k-NN across 9 downstream crates, plus a wide correctness sweep. 12,721 tests passing, 36 crates, >99% scikit-learn API coverage held.

release sklears machine-learning scikit-learn rust classical-ml cuda gpu oxicuda

A GPU backend that runs real kernels across nine crates is a big deal. Just as big: a release that went looking for its own fake results and fixed every one it found.

Today we released sklears 0.2.0 — the release that replaces sklears’s GPU code, previously a set of stubs and always-CPU fallbacks wearing a GPU-shaped API, with a real CUDA foundation built on oxicuda. It’s consumed by nine downstream crates, not a single showcase demo, and it shipped alongside a correctness sweep that hunted down and fixed several results across the workspace that had been silently fabricated.

sklears is the pure-Rust alternative to scikit-learn: No C. No Fortran. No Cython. No GIL. scikit-learn’s own pipeline depends on NumPy, SciPy, and a zoo of compiled extensions — and GPU acceleration usually means reaching for a separate stack (cuML/RAPIDS) with its own CUDA Toolkit and conda-environment requirements. sklears is plain Rust on the SciRS2 stack, with OxiBLAS for BLAS/LAPACK, Oxicode for serialization, and now oxicuda for GPU — which loads libcuda.so/nvcuda.dll dynamically at runtime rather than requiring the CUDA SDK or a C/C++ compiler to build. Compiles to a single static binary (or WASM) and runs everywhere, GPU or no GPU.

Why sklears 0.2.0 is a game changer

GPU acceleration for classical ML in the Python world means:

sklears 0.2.0 ends all of that:

Technical Deep Dive: what’s under the hood

  1. The foundation layer (sklears-core::gpu). GpuBackend pairs an oxicuda_driver::Context with an oxicuda_blas::BlasHandle. GpuArray<T: Copy> is backed by a real oxicuda_memory::DeviceBuffer<T> — there’s no CPU-fallback path hiding inside the type itself, because a GpuArray can only be constructed from an already-detected GpuBackend. GpuMatrixOps (matmul/add/mul/scale/transpose) dispatches straight to oxicuda-blas kernels.

  2. Per-algorithm dispatch, honestly optional. Each of the nine consuming crates gates its GPU path behind its own gpu Cargo feature (e.g. sklears-svm’s gpu = ["dep:oxicuda-blas", "dep:oxicuda-memory", "sklears-core/gpu_support"]) and stores its context as Option<GpuBackend>. sklears-svm’s RBF/Sigmoid kernel post-processing now runs entirely on-device instead of downloading the inner-product matrix first; sklears-neural’s tensor_core_gemm_f16/mixed_precision_gemm perform real FP16 tensor-core GEMM via oxicuda_blas::level3::gemm::<half::f16>.

  3. Heavier solves via oxicuda-solver/oxicuda-manifold. sklears-decomposition’s gpu_svd/gpu_eigendecomposition now call oxicuda_solver::dense::{svd, syevd} with CPU (scirs2_linalg) fallback on failure; sklears-linear’s GPU LU-solve and Householder QR go through oxicuda_solver::dense::{lu_factorize, lu_solve, qr_factorize, qr_generate_q}; sklears-manifold’s GpuTSNE::fit_transform runs the real oxicuda_manifold::tsne_fit optimizer and its GpuAccelerator::knn_search gains an HNSW-backed approximate path, and sklears-neighbors’s GpuKNeighborsSearch::with_ann gets the same HNSW-backed route with automatic brute-force fallback for metrics HNSW doesn’t cover.

  4. A parallel correctness sweep, including real memory-safety fixes. Several 0.2.0 fixes are Miri-detected soundness bugs, not GPU-related: AlignedAllocator::aligned_vec and MemoryLayoutManager::allocate_aligned both wrapped custom-aligned allocations in a Vec/Box whose Drop frees using the natural (smaller) alignment — an alloc/dealloc mismatch — now fixed with dedicated aligned-buffer types that free themselves with their real Layout.

Getting Started

cargo add sklears

The base CPU API is unchanged — plain training still looks like this:

use sklears::prelude::*;
use sklears::linear_model::LinearRegression;
use sklears::model_selection::train_test_split;

fn main() -> Result<()> {
    let dataset = sklears::dataset::make_regression(100, 10, 0.1)?;
    let (x_train, x_test, y_train, y_test) =
        train_test_split(&dataset.data, &dataset.target, 0.2, Some(42))?;

    let model = LinearRegression::new()
        .fit_intercept(true)
        .fit(&x_train, &y_train)?;

    let r2_score = model.score(&x_test, &y_test)?;
    println!("R² score: {:.4}", r2_score);
    Ok(())
}

0.2.0’s new part is the honest GPU foundation, reachable directly from sklears-core once a crate’s gpu feature is enabled:

use sklears_core::gpu::{GpuArray, GpuBackend, GpuMatrixOps};

// detect() genuinely returns Ok(None) with no CUDA driver present --
// it never substitutes a CPU computation dressed up as a GPU result.
if let Some(backend) = GpuBackend::detect()? {
    let a = GpuArray::from_array2(&backend, &x)?;
    let b = GpuArray::from_array2(&backend, &weights)?;
    let product = a.matmul(&b)?;        // real on-device GEMM via oxicuda-blas
    let result = product.to_array2()?;  // download back to host
    println!("GPU result shape: {:?}", result.shape());
} else {
    println!("No CUDA device -- falling back to CPU/Rayon automatically");
}

What’s New in 0.2.0

Tips

This is the foundation

As of July 14, 2026, sklears sits on:

Beyond the core, sklears feeds into TenfloweRS and TrustformeRS for deep learning, and alongside Celers for streaming data — one pure-Rust stack from raw data through trained models, GPU or no GPU, no Python runtime required.

Repository: https://github.com/cool-japan/sklears

Star the repo if a classical-ML library that tells you the truth about whether your GPU is actually being used is something you want to build on — every star helps the ecosystem grow.

The era of “GPU support” meaning a second Python package and a CUDA Toolkit install is over. Pure Rust machine learning is here — fast, safe, and sovereign.

KitaSan at COOLJAPAN OÜ July 14, 2026

↑ Back to all posts