Repository navigation
fix(coverage): close final branch gaps in evidence and network estimators #241
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
a12e48c
5d69239
ba0dd82
83e1558
e49cecc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -133,21 +133,21 @@ pub fn consensus_clusters( | |||||||||||||||||||||||||||||||||||||||||||||
| if assignments[i].is_some() { | ||||||||||||||||||||||||||||||||||||||||||||||
| continue; | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| // Find all j ≥ i still unassigned whose co-assignment with i | ||||||||||||||||||||||||||||||||||||||||||||||
| // reaches the threshold. | ||||||||||||||||||||||||||||||||||||||||||||||
| // Every candidate j in i..k is still unassigned here: topics | ||||||||||||||||||||||||||||||||||||||||||||||
| // below i were finalized earlier and the loop never revisits | ||||||||||||||||||||||||||||||||||||||||||||||
| // them, so membership reduces to the co-assignment threshold. | ||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+136
to
+138
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Comment wrongly calls a required guard redundant The comment claims every candidate
Suggested change
Was this helpful? React with 👍 or 👎 to provide feedback. |
||||||||||||||||||||||||||||||||||||||||||||||
| let members: Vec<usize> = (i..k_topics) | ||||||||||||||||||||||||||||||||||||||||||||||
| .filter(|&j| { | ||||||||||||||||||||||||||||||||||||||||||||||
| assignments[j].is_none() | ||||||||||||||||||||||||||||||||||||||||||||||
| && co_count[i][j] as f64 / n_replicates as f64 >= consensus_threshold | ||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||
| .collect(); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| // Self-coassignment is identically one, so i itself is always a | ||||||||||||||||||||||||||||||||||||||||||||||
| // member; a lone member forms a cluster only when the topic | ||||||||||||||||||||||||||||||||||||||||||||||
| // actually appears in the perturbed graph. | ||||||||||||||||||||||||||||||||||||||||||||||
| if members.len() < 2 { | ||||||||||||||||||||||||||||||||||||||||||||||
| // Singletons stay unclustered unless they pair strongly. | ||||||||||||||||||||||||||||||||||||||||||||||
| if !members.is_empty() | ||||||||||||||||||||||||||||||||||||||||||||||
| && co_count[i][i] as f64 / n_replicates as f64 >= consensus_threshold | ||||||||||||||||||||||||||||||||||||||||||||||
| && adjacency.contains_key(&i) | ||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||
| if adjacency.contains_key(&i) { | ||||||||||||||||||||||||||||||||||||||||||||||
| assignments[i] = Some(next_cluster); | ||||||||||||||||||||||||||||||||||||||||||||||
| next_cluster += 1; | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
145
to
153
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📝 Info: Consensus singleton guards removed are always-true The dropped conditions (Refers to this code) Was this helpful? React with 👍 or 👎 to provide feedback. |
||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -317,11 +317,49 @@ mod tests { | |||||||||||||||||||||||||||||||||||||||||||||
| // its co-assignment stays well above the consensus threshold | ||||||||||||||||||||||||||||||||||||||||||||||
| // while never reaching a deterministic 1.0. | ||||||||||||||||||||||||||||||||||||||||||||||
| let within_pair = output.co_assignment[0][1]; | ||||||||||||||||||||||||||||||||||||||||||||||
| assert!(within_pair > 0.6 && within_pair < 1.0, "co = {within_pair}"); | ||||||||||||||||||||||||||||||||||||||||||||||
| assert!(within_pair > 0.6, "co = {within_pair}"); | ||||||||||||||||||||||||||||||||||||||||||||||
| assert!(within_pair < 1.0, "co = {within_pair}"); | ||||||||||||||||||||||||||||||||||||||||||||||
| // Cross-pair topics never co-assign: they share no edge. | ||||||||||||||||||||||||||||||||||||||||||||||
| assert!((output.co_assignment[0][2]).abs() < 1e-12); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| #[test] | ||||||||||||||||||||||||||||||||||||||||||||||
| fn heavily_perturbed_single_edge_leaves_both_topics_unclustered() { | ||||||||||||||||||||||||||||||||||||||||||||||
| // With a 0.9 drop probability the only edge survives ~10% of | ||||||||||||||||||||||||||||||||||||||||||||||
| // replicates, so pair co-assignment stays below the threshold; | ||||||||||||||||||||||||||||||||||||||||||||||
| // each topic still carries graph adjacency and therefore forms | ||||||||||||||||||||||||||||||||||||||||||||||
| // its own singleton cluster instead of disappearing. | ||||||||||||||||||||||||||||||||||||||||||||||
| let single = vec![edge(0, 1, 0.99)]; | ||||||||||||||||||||||||||||||||||||||||||||||
| let output = consensus_clusters(&single, 2, 60, 0.5, 0.9, 41).unwrap(); | ||||||||||||||||||||||||||||||||||||||||||||||
| assert_ne!(output.assignments[0], None); | ||||||||||||||||||||||||||||||||||||||||||||||
| assert_ne!(output.assignments[1], None); | ||||||||||||||||||||||||||||||||||||||||||||||
| assert_ne!(output.assignments[0], output.assignments[1]); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+327
to
+337
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win 테스트 이름을 검증 결과와 일치시키세요.
테스트 이름 수정안- fn heavily_perturbed_single_edge_leaves_both_topics_unclustered() {
+ fn heavily_perturbed_single_edge_forms_distinct_singleton_clusters() {📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| #[test] | ||||||||||||||||||||||||||||||||||||||||||||||
| fn spanning_cluster_forces_rescan_over_assigned_topics() { | ||||||||||||||||||||||||||||||||||||||||||||||
| // A single edge joins topics 0 and 2 while topic 1 stays | ||||||||||||||||||||||||||||||||||||||||||||||
| // isolated. After the first iteration assigns {0, 2}, the scan | ||||||||||||||||||||||||||||||||||||||||||||||
| // for topic 1 must walk past an already-assigned topic before | ||||||||||||||||||||||||||||||||||||||||||||||
| // finishing, exercising the assigned-skip arm of the membership | ||||||||||||||||||||||||||||||||||||||||||||||
| // filter. | ||||||||||||||||||||||||||||||||||||||||||||||
| let skip_pair = vec![edge(0, 2, 0.97)]; | ||||||||||||||||||||||||||||||||||||||||||||||
| let output = consensus_clusters(&skip_pair, 3, 20, 0.9, 0.0, 5).unwrap(); | ||||||||||||||||||||||||||||||||||||||||||||||
| assert_eq!(output.assignments[0], output.assignments[2]); | ||||||||||||||||||||||||||||||||||||||||||||||
| assert_eq!(output.assignments[1], None); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| #[test] | ||||||||||||||||||||||||||||||||||||||||||||||
| fn isolated_topic_without_edges_stays_unclustered() { | ||||||||||||||||||||||||||||||||||||||||||||||
| let chain = vec![edge(0, 1, 0.95), edge(1, 2, 0.9)]; | ||||||||||||||||||||||||||||||||||||||||||||||
| let output = consensus_clusters(&chain, 4, 25, 0.95, 0.0, 3).unwrap(); | ||||||||||||||||||||||||||||||||||||||||||||||
| assert!(output.assignments[0].is_some()); | ||||||||||||||||||||||||||||||||||||||||||||||
| assert!(output.assignments[1].is_some()); | ||||||||||||||||||||||||||||||||||||||||||||||
| assert!(output.assignments[2].is_some()); | ||||||||||||||||||||||||||||||||||||||||||||||
| assert_eq!(output.assignments[3], None); | ||||||||||||||||||||||||||||||||||||||||||||||
| assert!((output.co_assignment[3][0]).abs() < 1e-12); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| #[test] | ||||||||||||||||||||||||||||||||||||||||||||||
| fn identical_inputs_and_seed_reproduce_output_exactly() { | ||||||||||||||||||||||||||||||||||||||||||||||
| let chain = vec![edge(0, 1, 0.9), edge(1, 2, 0.8)]; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -118,10 +118,11 @@ pub fn bootstrap_edge_stability( | |
| for j in (i + 1)..k { | ||
| if admitted_set.contains(&(i, j)) { | ||
| admitted_count[pair] += 1; | ||
| let full_sign = full_rs[pair].signum(); | ||
| let replicate_sign = rs[pair].signum(); | ||
| #[allow(clippy::float_cmp)] | ||
| if full_sign != 0.0 && replicate_sign == full_sign { | ||
| // Sign agreement uses the signum product so both the | ||
| // matching and the flipped outcome are ordinary | ||
| // arithmetic branches rather than a float equality. | ||
| let sign_agreement = full_rs[pair].signum() * rs[pair].signum(); | ||
| if sign_agreement > 0.0 { | ||
| sign_match_count[pair] += 1; | ||
|
Comment on lines
+121
to
126
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📝 Info: Signum-product sign match is equivalent to old check
Was this helpful? React with 👍 or 👎 to provide feedback. |
||
| } | ||
| } | ||
|
|
@@ -275,6 +276,42 @@ mod tests { | |
| } | ||
| } | ||
|
|
||
| #[test] | ||
| fn sign_flips_under_noise_reduce_stability() { | ||
| // Independent LCG noise makes every pair's sample correlation a | ||
| // near-zero draw; with admission at zero and a permissive FDR | ||
| // level every replicate admits all edges, and resampling flips | ||
| // signs often enough that reported stability is partial. | ||
| let mut state = 0x5EED_1234_ABCD_9876_u64; | ||
| let mut next_unit = move || { | ||
| state = state | ||
| .wrapping_mul(6_364_136_223_846_793_005) | ||
| .wrapping_add(1_442_695_040_888_963_407); | ||
| ((state >> 33) as f64) / ((u64::MAX >> 33) as f64) | ||
| }; | ||
| let draws: Vec<Vec<f64>> = (0..24) | ||
| .map(|i| { | ||
| let signal = f64::from(i) / 24.0; | ||
| vec![ | ||
| // One perfectly aligned pair keeps the ceiling arm | ||
| // of the stability assertion exercised. | ||
| signal, | ||
| signal + 1e-12, | ||
| next_unit() - 0.5, | ||
| ] | ||
| }) | ||
| .collect(); | ||
| let scores = bootstrap_edge_stability(&draws, 0.0, 1.0, 80, 29).unwrap(); | ||
| // The aligned pair is perfectly stable (ceiling arm); each | ||
| // noise pair flips sign in at least one resample (strictly | ||
| // below the ceiling) while remaining mostly consistent. | ||
| assert!((scores[0].stability - 1.0).abs() < f64::EPSILON); | ||
| for score in &scores[1..] { | ||
| assert!(score.stability > 0.05, "{score:?}"); | ||
| assert!(score.stability < 0.999, "{score:?}"); | ||
|
Comment on lines
+306
to
+309
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 안정성 테스트의 기대 범위를 더 좁히세요. 현재 고정된 LCG 시드에 대한 기대 일치·불일치 횟수 또는 더 좁은 기준값을 검증하세요. 각 잡음 엣지에 최소 한 번의 일치와 불일치가 발생했는지도 명시적으로 확인하세요. As per coding guidelines: 과학적 수용 테스트는 현실적인 synthetic truth와 변경된 의미를 검증해야 합니다. 🤖 Prompt for AI AgentsSource: Coding guidelines |
||
| } | ||
| } | ||
|
|
||
| #[test] | ||
| fn bootstrap_is_deterministic_per_seed() { | ||
| let draws: Vec<Vec<f64>> = (0..40) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📝 Info: unwrap_or_default preserves the media-type refusal
When
strip_prefix("image/")fails, the oldlet-elsereturned false; the newunwrap_or_default()gives an empty subtype that fails!subtype.is_empty(), so the result is the same. The scanner only yieldsdata:image/candidates, so the strip never actually fails.Was this helpful? React with 👍 or 👎 to provide feedback.