Switch From ndarray/nalgebra/linfa
Contract:
apr-book-ch26
Run: cargo run -p aprender-core --example ch26_switch_ndarray
#![allow(clippy::disallowed_methods)]
//! Chapter 26: Switch From ndarray/nalgebra/linfa
//!
//! API equivalence: ndarray/linfa → aprender
//! Contract: contracts/apr-book-ch26-v1.yaml
use aprender::prelude::*;
use aprender::metrics::classification::accuracy;
fn main() {
println!("=== Switch From ndarray/nalgebra/linfa ===");
println!();
// API equivalence table
println!("| ndarray/linfa | aprender |");
println!("|--------------------------------|------------------------------------|");
println!("| Array2::from_shape_vec((r,c)) | Matrix::from_vec(r, c, vec) |");
println!("| Array1::from_vec(vec) | Vector::from_slice(&vec) |");
println!("| a.dot(&b) | a.dot(&b) |");
println!("| linfa::KMeans::params(k) | KMeans::new(k) |");
println!("| linfa_linear::LinearRegression | LinearRegression::new() |");
println!("| dataset.fit(&model) | model.fit(&x, &y) |");
println!("| model.predict(&x) | model.predict(&x) |");
println!();
// Matrix operations — same patterns
let m = Matrix::from_vec(3, 2, vec![
1.0, 2.0, 3.0, 4.0, 5.0, 6.0,
]).expect("valid 3x2 matrix");
let v = Vector::from_slice(&[1.0, 1.0]);
let result = m.matvec(&v).expect("matvec");
println!("Matrix (3x2) @ Vector (2) = [{:.0}, {:.0}, {:.0}]",
result[0], result[1], result[2]);
assert!((result[0] - 3.0).abs() < 1e-4, "matvec contract");
// LinearRegression — same API pattern as linfa
let x = Matrix::from_vec(4, 1, vec![1.0, 2.0, 3.0, 4.0]).expect("valid");
let y = Vector::from_slice(&[2.0, 4.0, 6.0, 8.0]);
let mut lr = LinearRegression::new();
lr.fit(&x, &y).expect("fit");
let pred = lr.predict(&x);
let r2 = lr.score(&x, &y);
println!();
println!("LinearRegression: R2 = {r2:.4}");
assert!(r2 > 0.99, "Perfect linear fit");
// KMeans — same pattern as linfa
let data = Matrix::from_vec(6, 2, vec![
1.0, 1.0, 1.1, 0.9, 0.9, 1.1,
5.0, 5.0, 5.1, 4.9, 4.9, 5.1,
]).expect("valid");
let mut km = KMeans::new(2).with_random_state(42);
km.fit(&data).expect("fit");
let labels = km.predict(&data);
println!("KMeans labels: {:?}", labels);
assert_eq!(labels[0], labels[1], "Cluster coherence");
assert_ne!(labels[0], labels[3], "Cluster separation");
// DecisionTree for classification
let x_cls = Matrix::from_vec(6, 2, vec![
1.0, 1.0, 2.0, 2.0, 3.0, 1.0,
6.0, 5.0, 7.0, 8.0, 8.0, 6.0,
]).expect("valid");
let y_cls: Vec<usize> = vec![0, 0, 0, 1, 1, 1];
let mut tree = DecisionTreeClassifier::new().with_max_depth(3);
tree.fit(&x_cls, &y_cls).expect("fit");
let preds = tree.predict(&x_cls);
let acc = accuracy(&preds, &y_cls);
println!("DecisionTree accuracy: {acc:.2}");
assert!(acc > 0.8, "Accuracy contract");
println!();
println!("Key differences from ndarray/linfa:");
println!(" 1. aprender uses f32 by default (ML-optimized, SIMD-friendly)");
println!(" 2. No Dataset wrapper needed — fit(&Matrix, &[label]) directly");
println!(" 3. 70 crates: compute, serve, train, contracts (not just ML)");
println!(" 4. cargo install aprender gives you `apr` CLI (57 commands)");
println!();
println!("Chapter 26 contracts: PASSED");
}