use blr_core::{features, fit, ArdConfig};
use std::path::Path;
fn main() {
let manifest_dir = env!("CARGO_MANIFEST_DIR");
let csv = Path::new(manifest_dir).join("data/hall_sensor_calibration.csv");
let content = std::fs::read_to_string(&csv)
.unwrap_or_else(|e| panic!("Failed to read {}: {}", csv.display(), e));
let mut x_vals: Vec<f64> = Vec::new();
let mut y_vals: Vec<f64> = Vec::new();
for line in content.lines().skip(1) {
let mut parts = line.split(',');
x_vals.push(parts.next().unwrap().trim().parse().unwrap());
y_vals.push(parts.next().unwrap().trim().parse().unwrap());
}
let n = x_vals.len();
let (poly_mat, _) = features::polynomial(&x_vals, 3); let mut phi = vec![0.0f64; n * 6];
for i in 0..n {
phi[i * 6] = poly_mat[i * 4]; phi[i * 6 + 1] = poly_mat[i * 4 + 1]; phi[i * 6 + 2] = poly_mat[i * 4 + 2]; phi[i * 6 + 3] = poly_mat[i * 4 + 3]; phi[i * 6 + 4] = (x_vals[i] / 0.8).tanh(); phi[i * 6 + 5] = (x_vals[i] / 1.5).tanh(); }
let config = ArdConfig {
max_iter: 500,
tol: 1e-7,
..ArdConfig::default()
};
let fitted = fit(&phi, &y_vals, n, 6, &config).expect("BLR+ARD fit failed");
let feature_names = ["1 (bias)", "x", "x²", "x³", "tanh(x/0.8)", "tanh(x/1.5)"];
println!("=== Hall Sensor BLR+ARD Results ===");
println!("EM iterations: {}", fitted.log_evidences.len());
println!("Noise std (learned): {:.6}", fitted.noise_std());
println!(
"Log marginal likelihood:{:.6}",
fitted.log_marginal_likelihood()
);
println!("\nPosterior mean weights:");
for (name, &mu_j) in feature_names.iter().zip(fitted.posterior.mean.iter()) {
println!(" {:<18} {:+.6}", name, mu_j);
}
println!("\nARD relevance (1/α — larger = more relevant):");
let rel = fitted.relevance();
for (name, r) in feature_names.iter().zip(rel.iter()) {
println!(" {:<18} {:.3e}", name, r);
}
println!("\nActive features (α < geometric-mean threshold):");
let active = fitted.relevant_features(None);
let mut n_active = 0;
for (name, &is_active) in feature_names.iter().zip(active.iter()) {
if is_active {
println!(" ✓ {}", name);
n_active += 1;
}
}
if n_active == 0 {
println!(" (none — try lowering the threshold)");
}
let preds = fitted.predict(&phi, n, 6);
let rmse = (preds
.mean
.iter()
.zip(y_vals.iter())
.map(|(p, y)| (p - y).powi(2))
.sum::<f64>()
/ n as f64)
.sqrt();
println!("\nIn-sample RMSE: {:.6}", rmse);
println!(
"Mean total std: {:.6}",
preds.total_std.iter().sum::<f64>() / n as f64
);
}