mirror of
https://github.com/ruvnet/RuView
synced 2026-08-07 20:01:43 +00:00
#613 fixed adaptive_classifier.rs:94 (the IQR sort) and called the audit done, but the grep used `partial_cmp(b).unwrap()` as a literal and missed seven additional production sites that use comparator variants: adaptive_classifier.rs:205 AdaptiveModel::classify() argmax over softmax probs — same per-frame hot path as #611. NaN flows through normalise → logits → softmax and still reaches this site even after the IQR fix. adaptive_classifier.rs:480 train() argmax (training accuracy loop) adaptive_classifier.rs:500 train() per-class argmax main.rs:2446, 2449 count_persons_mincut variance source/sink select csi.rs:602, 605 count_persons_mincut variance source/sink select (duplicate of main.rs logic in csi.rs) For the variance-select sites, note that the *outer* `unwrap_or((0, &0))` only catches an empty iterator — it cannot rescue a panic raised inside the comparator. A single NaN in `variances[]` still aborts the process. Same fix as #613: swap `.unwrap()` for `.unwrap_or(std::cmp::Ordering::Equal)` inside the comparator closure. Pure behavioural change, no API surface. Re-audit of the remaining `partial_cmp(...).unwrap()` matches in v2/: they are all inside `#[cfg(test)]` / `#[test]` blocks (spectrogram.rs:269, depth.rs:234, connectivity.rs:477, vital_signs.rs:737) where inputs are controlled and panic-on-NaN is acceptable.
This commit is contained in:
@@ -200,9 +200,11 @@ impl AdaptiveModel {
|
||||
probs[c] = ((logits[c] - max_logit).exp()) / exp_sum;
|
||||
}
|
||||
|
||||
// Pick argmax.
|
||||
// Pick argmax. Same NaN-panic class as #611: if any raw_feature is NaN
|
||||
// it propagates through normalize → logits → softmax, then partial_cmp
|
||||
// returns None and unwrap() panics the sensing server on every frame.
|
||||
let (best_c, best_p) = probs.iter().enumerate()
|
||||
.max_by(|a, b| a.1.partial_cmp(b.1).unwrap())
|
||||
.max_by(|a, b| a.1.partial_cmp(b.1).unwrap_or(std::cmp::Ordering::Equal))
|
||||
.unwrap();
|
||||
let label = if best_c < self.class_names.len() {
|
||||
self.class_names[best_c].clone()
|
||||
@@ -477,7 +479,7 @@ pub fn train_from_recordings(recordings_dir: &Path) -> Result<AdaptiveModel, Str
|
||||
}
|
||||
}
|
||||
let pred = logits.iter().enumerate()
|
||||
.max_by(|a, b| a.1.partial_cmp(b.1).unwrap())
|
||||
.max_by(|a, b| a.1.partial_cmp(b.1).unwrap_or(std::cmp::Ordering::Equal))
|
||||
.unwrap().0;
|
||||
if pred == *target { correct += 1; }
|
||||
}
|
||||
@@ -497,7 +499,7 @@ pub fn train_from_recordings(recordings_dir: &Path) -> Result<AdaptiveModel, Str
|
||||
}
|
||||
}
|
||||
let pred = logits.iter().enumerate()
|
||||
.max_by(|a, b| a.1.partial_cmp(b.1).unwrap())
|
||||
.max_by(|a, b| a.1.partial_cmp(b.1).unwrap_or(std::cmp::Ordering::Equal))
|
||||
.unwrap().0;
|
||||
if pred == *target { class_correct[*target] += 1; }
|
||||
}
|
||||
|
||||
@@ -598,11 +598,13 @@ pub fn estimate_persons_from_correlation(frame_history: &VecDeque<Vec<f64>>) ->
|
||||
}
|
||||
}
|
||||
|
||||
// partial_cmp returns None on NaN; the outer unwrap_or only catches an
|
||||
// empty iterator, not a comparator panic. Same NaN-panic class as #611.
|
||||
let (max_var_idx, _) = active.iter().enumerate()
|
||||
.max_by(|(_, &a), (_, &b)| variances[a].partial_cmp(&variances[b]).unwrap())
|
||||
.max_by(|(_, &a), (_, &b)| variances[a].partial_cmp(&variances[b]).unwrap_or(std::cmp::Ordering::Equal))
|
||||
.unwrap_or((0, &0));
|
||||
let (min_var_idx, _) = active.iter().enumerate()
|
||||
.min_by(|(_, &a), (_, &b)| variances[a].partial_cmp(&variances[b]).unwrap())
|
||||
.min_by(|(_, &a), (_, &b)| variances[a].partial_cmp(&variances[b]).unwrap_or(std::cmp::Ordering::Equal))
|
||||
.unwrap_or((0, &0));
|
||||
if max_var_idx == min_var_idx { return 1; }
|
||||
|
||||
|
||||
@@ -2559,12 +2559,15 @@ fn estimate_persons_from_correlation(frame_history: &VecDeque<Vec<f64>>) -> usiz
|
||||
}
|
||||
}
|
||||
|
||||
// Source → highest-variance subcarrier, Sink → lowest-variance
|
||||
// Source → highest-variance subcarrier, Sink → lowest-variance.
|
||||
// partial_cmp returns None on NaN; the outer unwrap_or only catches an
|
||||
// empty iterator, not a comparator panic. Same NaN-panic class as #611
|
||||
// — a single NaN variance frame would kill the sensing-server process.
|
||||
let (max_var_idx, _) = active.iter().enumerate()
|
||||
.max_by(|(_, &a), (_, &b)| variances[a].partial_cmp(&variances[b]).unwrap())
|
||||
.max_by(|(_, &a), (_, &b)| variances[a].partial_cmp(&variances[b]).unwrap_or(std::cmp::Ordering::Equal))
|
||||
.unwrap_or((0, &0));
|
||||
let (min_var_idx, _) = active.iter().enumerate()
|
||||
.min_by(|(_, &a), (_, &b)| variances[a].partial_cmp(&variances[b]).unwrap())
|
||||
.min_by(|(_, &a), (_, &b)| variances[a].partial_cmp(&variances[b]).unwrap_or(std::cmp::Ordering::Equal))
|
||||
.unwrap_or((0, &0));
|
||||
|
||||
if max_var_idx == min_var_idx {
|
||||
|
||||
Reference in New Issue
Block a user