use super::*;
use crate::lfq::LfqEntry;
fn entry(feature_idx: usize, run_idx: usize, score: f32, is_decoy: bool) -> LfqEntry {
LfqEntry {
owned_samples: 0,
preceding_signal_fraction: 0.0,
excluded_samples: 0,
competing_feature: None,
ownership_status: "exclusive",
feature_idx,
run_idx,
intensity: 1.0,
hybrid_score: score,
spectral_bhattacharyya: 0.0,
n_isotopes_found: 0,
rt_score: 0.0,
int_score: 0.0,
coelution: 0.0,
is_decoy,
is_mbr: false,
expected_rt: 0.0,
apex_rt: 0.0,
peak_width_rt: 0.0,
expected_mz: 0.0,
observed_mz: 0.0,
expected_im: 0.0,
observed_im: 0.0,
}
}
#[test]
fn all_targets_get_zero_q() {
let entries = vec![
entry(0, 0, 0.9, false),
entry(1, 0, 0.8, false),
entry(2, 0, 0.7, false),
];
let q = compute_qvalues(&entries);
assert_eq!(q.len(), 3);
for v in q.values() {
assert_eq!(*v, 0.0);
}
}
#[test]
fn running_fdr_counts_decoys_over_targets_and_monotonises() {
let entries = vec![
entry(0, 0, 1.0, false),
entry(9, 0, 0.9, true),
entry(1, 0, 0.8, false),
entry(2, 0, 0.7, false),
];
let q = compute_qvalues(&entries);
assert!((q[&(0, 0)] - 0.0).abs() < 1e-9);
assert!(
(q[&(1, 0)] - 1.0 / 3.0).abs() < 1e-9,
"q was {}",
q[&(1, 0)]
);
assert!(
(q[&(2, 0)] - 1.0 / 3.0).abs() < 1e-9,
"q was {}",
q[&(2, 0)]
);
assert_eq!(q.len(), 3);
}
#[test]
fn qvalues_monotone_in_score() {
let entries = vec![
entry(0, 0, 0.95, false),
entry(1, 0, 0.90, true),
entry(2, 0, 0.85, false),
entry(3, 0, 0.80, false),
entry(4, 0, 0.75, true),
entry(5, 0, 0.70, false),
];
let q = compute_qvalues(&entries);
let qs: Vec<f64> = [(0, 0), (2, 0), (3, 0), (5, 0)]
.iter()
.map(|k| q[k])
.collect();
for w in qs.windows(2) {
assert!(
w[1] >= w[0] - 1e-12,
"q not monotone non-decreasing: {qs:?}"
);
}
}