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 2 commits
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) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -483,14 +483,21 @@ def _is_multiline_match_guard(lines: list[str], line_number: int) -> bool: | |
|
|
||
|
|
||
| def _cfg_test_module_line_numbers(lines: list[str]) -> set[int]: | ||
| """Return line numbers belonging to any ``#[cfg(test)] mod ... { ... }`` block.""" | ||
| """Return line numbers belonging to any ``#[cfg(test)] mod ... { ... }`` block. | ||
|
|
||
| Blank lines and further attributes (for example ``#[allow(...)]``) may sit | ||
| between ``#[cfg(test)]`` and the ``mod`` declaration; both belong to the | ||
| module and must not break detection. | ||
| """ | ||
|
|
||
| test_lines: set[int] = set() | ||
| index = 0 | ||
| while index < len(lines): | ||
| if lines[index].strip().startswith("#[cfg(test)]"): | ||
| look = index + 1 | ||
| while look < len(lines) and not lines[look].strip(): | ||
| while look < len(lines) and ( | ||
| not lines[look].strip() or lines[look].strip().startswith("#[") | ||
| ): | ||
|
Comment on lines
+498
to
+500
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 🔎 Supported by static analysis🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- target function and nearby code ---'
sed -n '450,535p' scripts/check_coverage.py
printf '%s\n' '--- definitions and callers ---'
rg -n -C 3 '_cfg_test_module_line_numbers|is_executable_source_line|coverage denominator|coverage' scripts/check_coverage.py tests 2>/dev/null | head -n 240
printf '%s\n' '--- candidate test files ---'
git ls-files | rg '(^|/)(test|tests|scripts)(/|$)|check_coverage'Repository: ContextualWisdomLab/TEPP Length of output: 32114 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- executable-line path ---'
sed -n '168,218p' scripts/check_coverage.py
printf '%s\n' '--- coverage tests: relevant symbols and surrounding code ---'
rg -n -C 8 '_cfg_test_module_line_numbers|cfg\(test\)|is_executable_source_line|mod tests|test module' tests/quality/test_check_coverage.pyRepository: ContextualWisdomLab/TEPP Length of output: 25087 🏁 Script executed: #!/bin/bash
set -eu
sed -n '218,285p' scripts/check_coverage.pyRepository: ContextualWisdomLab/TEPP Length of output: 2301 다중 행 속성을 건너뛰도록 보강하세요.
🤖 Prompt for AI Agents |
||
| look += 1 | ||
| if look < len(lines) and lines[look].strip().startswith("mod "): | ||
|
Comment on lines
+498
to
502
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. 🔍 Attribute-skip can over-capture body-less mod decls The skip loop now advances past attributes between Was this helpful? React with 👍 or 👎 to provide feedback. |
||
| depth = 0 | ||
|
|
||
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.