Skip to main content

digits/
digits.rs

1//! Real-world benchmark: classify handwritten digits (8x8, the sklearn "digits" dataset)
2//! with an HDC classifier — one-shot vs adaptive retraining.
3//!
4//! Run with:  cargo run --release --example digits
5
6use holos_core::{Classifier, Hypervector, ItemMemory, LevelEncoder, Rng};
7
8fn main() {
9    // Dataset shipped with the crate: 64 pixel values (0..16) + label, per line.
10    let csv = include_str!("data/digits.csv");
11    let mut rows: Vec<(Vec<f64>, String)> = Vec::new();
12    for line in csv.lines() {
13        if line.trim().is_empty() {
14            continue;
15        }
16        let mut nums: Vec<i64> = line.split(',').map(|t| t.trim().parse().unwrap()).collect();
17        let label = nums.pop().unwrap().to_string();
18        let features: Vec<f64> = nums.iter().map(|&v| v as f64).collect();
19        rows.push((features, label));
20    }
21
22    // Deterministic shuffle, then a 70/30 train/test split.
23    let mut rng = Rng::new(7);
24    for k in (1..rows.len()).rev() {
25        let j = (rng.next_u64() as usize) % (k + 1);
26        rows.swap(k, j);
27    }
28    let n_train = rows.len() * 7 / 10;
29    let (train, test) = rows.split_at(n_train);
30
31    // Encode: 64 features, pixel intensity 0..16 quantized to 16 levels.
32    let d = 10_000;
33    let enc = LevelEncoder::new(d, 64, 0.0, 16.0, 16, &mut rng);
34
35    let train_hv: Vec<Hypervector> = train.iter().map(|(f, _)| enc.encode(f)).collect();
36    let train_labels: Vec<&str> = train.iter().map(|(_, l)| l.as_str()).collect();
37    let test_hv: Vec<Hypervector> = test.iter().map(|(f, _)| enc.encode(f)).collect();
38    let test_labels: Vec<&str> = test.iter().map(|(_, l)| l.as_str()).collect();
39
40    let accuracy = |model: &ItemMemory| -> f64 {
41        let correct = test_hv
42            .iter()
43            .zip(&test_labels)
44            .filter(|(hv, &truth)| model.cleanup(hv).unwrap().0 == truth)
45            .count();
46        correct as f64 / test_hv.len() as f64
47    };
48
49    // One-shot training (single pass).
50    let mut clf = Classifier::new(d);
51    for (hv, &l) in train_hv.iter().zip(&train_labels) {
52        clf.train(hv, l);
53    }
54    let acc_oneshot = accuracy(&clf.build());
55
56    // Adaptive retraining.
57    let mut clf2 = Classifier::new(d);
58    clf2.fit(&train_hv, &train_labels, 20);
59    let acc_retrained = accuracy(&clf2.build());
60
61    println!(
62        "Handwritten digits: {} train / {} test, 10 classes",
63        train.len(),
64        test.len()
65    );
66    println!("HDC one-shot      : {:.1}%", 100.0 * acc_oneshot);
67    println!("HDC + retraining  : {:.1}%", 100.0 * acc_retrained);
68}