fdars_core/explain_generic/
lime.rs1use crate::error::FdarError;
2use crate::explain::{compute_lime, LimeResult};
3use crate::matrix::FdMatrix;
4
5use super::FpcPredictor;
6
7#[must_use = "expensive computation whose result should not be discarded"]
19pub fn generic_lime(
20 model: &dyn FpcPredictor,
21 data: &FdMatrix,
22 _scalar_covariates: Option<&FdMatrix>,
23 observation: usize,
24 n_samples: usize,
25 kernel_width: f64,
26 seed: u64,
27) -> Result<LimeResult, FdarError> {
28 let (n, m) = data.shape();
29 if observation >= n {
30 return Err(FdarError::InvalidParameter {
31 parameter: "observation",
32 message: format!("observation {observation} >= n {n}"),
33 });
34 }
35 if m != model.fpca_mean().len() {
36 return Err(FdarError::InvalidDimension {
37 parameter: "data columns",
38 expected: model.fpca_mean().len().to_string(),
39 actual: m.to_string(),
40 });
41 }
42 if n_samples == 0 {
43 return Err(FdarError::InvalidParameter {
44 parameter: "n_samples",
45 message: "n_samples must be > 0".into(),
46 });
47 }
48 if kernel_width <= 0.0 {
49 return Err(FdarError::InvalidParameter {
50 parameter: "kernel_width",
51 message: format!("kernel_width must be > 0, got {kernel_width}"),
52 });
53 }
54 let ncomp = model.ncomp();
55 if ncomp == 0 {
56 return Err(FdarError::InvalidParameter {
57 parameter: "ncomp",
58 message: "model has 0 components".into(),
59 });
60 }
61 let scores = model.project(data);
62 let obs_scores: Vec<f64> = (0..ncomp).map(|k| scores[(observation, k)]).collect();
63
64 let mut score_sd = vec![0.0; ncomp];
65 for k in 0..ncomp {
66 let mut ss = 0.0;
67 for i in 0..n {
68 let s = scores[(i, k)];
69 ss += s * s;
70 }
71 score_sd[k] = (ss / (n - 1).max(1) as f64).sqrt().max(1e-10);
72 }
73
74 let predict = |s: &[f64]| -> f64 { model.predict_from_scores(s, None) };
75
76 compute_lime(
77 &obs_scores,
78 &score_sd,
79 ncomp,
80 n_samples,
81 kernel_width,
82 seed,
83 observation,
84 &predict,
85 )
86 .ok_or_else(|| FdarError::ComputationFailed {
87 operation: "generic_lime",
88 detail: "LIME computation failed; the perturbation neighborhood may be too narrow — try increasing kernel_width or n_samples".into(),
89 })
90}