Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/nextclade/src/run/nextclade_run_one.rs
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,8 @@ pub fn nextclade_run_one(
nearest_node_name,
nearest_nodes,
} = if let Some(graph) = graph {
let nearest_node_candidates = graph_find_nearest_nodes(graph, &substitutions, &missing, &alignment_range)?;
let nearest_node_candidates =
graph_find_nearest_nodes(graph, &substitutions, &missing, &deletions, &alignment_range)?;
let nearest_node_id = nearest_node_candidates[0].node_key;
let nearest_node = graph.get_node(nearest_node_id)?.payload();
let nearest_node_name = nearest_node.name.clone();
Expand Down
141 changes: 129 additions & 12 deletions packages/nextclade/src/tree/tree_find_nearest_node.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::alphabet::nuc::Nuc;
use crate::analyze::is_sequenced::is_nuc_sequenced;
use crate::analyze::letter_ranges::NucRange;
use crate::analyze::nuc_del::NucDelRange;
use crate::analyze::nuc_sub::NucSub;
use crate::coord::range::NucRefGlobalRange;
use crate::graph::node::GraphNodeKey;
Expand All @@ -21,6 +22,7 @@ pub fn graph_find_nearest_nodes(
graph: &AuspiceGraph,
qry_nuc_subs: &[NucSub],
qry_missing: &[NucRange],
qry_deletions: &[NucDelRange],
aln_range: &NucRefGlobalRange,
) -> Result<Vec<TreePlacementInfo>, Report> {
let masked_ranges = graph.data.meta.placement_mask_ranges();
Expand All @@ -29,7 +31,14 @@ pub fn graph_find_nearest_nodes(
let nodes_by_placement_score = DftPre::new(graph.get_exactly_one_root()?, |node| graph.iter_children_of(node))
.map(|(_, node)| {
let node_payload = node.payload();
let distance = tree_calculate_node_distance(node_payload, qry_nuc_subs, qry_missing, aln_range, masked_ranges);
let distance = tree_calculate_node_distance(
node_payload,
qry_nuc_subs,
qry_missing,
qry_deletions,
aln_range,
masked_ranges,
);
let prior = get_prior(node_payload);
TreePlacementInfo {
node_key: node.key(),
Expand Down Expand Up @@ -69,6 +78,7 @@ fn tree_calculate_node_distance(
node: &AuspiceGraphNodePayload,
qry_nuc_subs: &[NucSub],
qry_missing: &[NucRange],
qry_deletions: &[NucDelRange],
aln_range: &NucRefGlobalRange,
masked_ranges: &[NucRefGlobalRange],
) -> i64 {
Expand Down Expand Up @@ -105,11 +115,13 @@ fn tree_calculate_node_distance(
}
}

// determine the number of sites that are mutated in the node but missing in seq.
// determine the number of sites that are mutated in the node but missing or deleted in seq.
// for these we can't tell whether the node agrees with seq
let mut undetermined_sites = 0_i64;
for pos in node.tmp.substitutions.keys() {
if !is_nuc_sequenced(*pos, &masked_qry_missing, aln_range) {
if !is_nuc_sequenced(*pos, &masked_qry_missing, aln_range)
|| qry_deletions.iter().any(|del| del.range().contains(*pos))
{
undetermined_sites += 1;
}
}
Comment on lines 117 to 127
Copy link
Copy Markdown
Member Author

@ivan-aksamentov ivan-aksamentov Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dels are new passed to distance metric calculation and counted same as unknowns or unsequenced. But I imagine it can also be treat differently if needed.

Expand Down Expand Up @@ -234,7 +246,15 @@ mod tests {
let aln_range = NucRefGlobalRange::from_usize(0, 100);
let masked_ranges = vec![];

let result = tree_calculate_node_distance(&node, &qry_nuc_subs, &qry_missing, &aln_range, &masked_ranges);
let qry_deletions: Vec<NucDelRange> = vec![];
let result = tree_calculate_node_distance(
&node,
&qry_nuc_subs,
&qry_missing,
&qry_deletions,
&aln_range,
&masked_ranges,
);

assert_eq!(result, 0);

Expand All @@ -249,7 +269,15 @@ mod tests {
let aln_range = NucRefGlobalRange::from_usize(0, 100);
let masked_ranges = vec![];

let result = tree_calculate_node_distance(&node, &qry_nuc_subs, &qry_missing, &aln_range, &masked_ranges);
let qry_deletions: Vec<NucDelRange> = vec![];
let result = tree_calculate_node_distance(
&node,
&qry_nuc_subs,
&qry_missing,
&qry_deletions,
&aln_range,
&masked_ranges,
);

assert_eq!(result, 3);

Expand All @@ -264,7 +292,15 @@ mod tests {
let aln_range = NucRefGlobalRange::from_usize(0, 100);
let masked_ranges = vec![];

let result = tree_calculate_node_distance(&node, &qry_nuc_subs, &qry_missing, &aln_range, &masked_ranges);
let qry_deletions: Vec<NucDelRange> = vec![];
let result = tree_calculate_node_distance(
&node,
&qry_nuc_subs,
&qry_missing,
&qry_deletions,
&aln_range,
&masked_ranges,
);

assert_eq!(result, 5);

Expand All @@ -279,7 +315,15 @@ mod tests {
let aln_range = NucRefGlobalRange::from_usize(0, 100);
let masked_ranges = vec![];

let result = tree_calculate_node_distance(&node, &qry_nuc_subs, &qry_missing, &aln_range, &masked_ranges);
let qry_deletions: Vec<NucDelRange> = vec![];
let result = tree_calculate_node_distance(
&node,
&qry_nuc_subs,
&qry_missing,
&qry_deletions,
&aln_range,
&masked_ranges,
);

assert_eq!(result, 5);

Expand All @@ -294,7 +338,15 @@ mod tests {
let aln_range = NucRefGlobalRange::from_usize(0, 100);
let masked_ranges = vec![];

let result = tree_calculate_node_distance(&node, &qry_nuc_subs, &qry_missing, &aln_range, &masked_ranges);
let qry_deletions: Vec<NucDelRange> = vec![];
let result = tree_calculate_node_distance(
&node,
&qry_nuc_subs,
&qry_missing,
&qry_deletions,
&aln_range,
&masked_ranges,
);

assert_eq!(result, 4);

Expand All @@ -309,7 +361,15 @@ mod tests {
let aln_range = NucRefGlobalRange::from_usize(0, 20);
let masked_ranges = vec![];

let result = tree_calculate_node_distance(&node, &qry_nuc_subs, &qry_missing, &aln_range, &masked_ranges);
let qry_deletions: Vec<NucDelRange> = vec![];
let result = tree_calculate_node_distance(
&node,
&qry_nuc_subs,
&qry_missing,
&qry_deletions,
&aln_range,
&masked_ranges,
);

assert_eq!(result, 3);

Expand All @@ -324,7 +384,15 @@ mod tests {
let aln_range = NucRefGlobalRange::from_usize(0, 100);
let masked_ranges = vec![NucRefGlobalRange::from_usize(0, 100)];

let result = tree_calculate_node_distance(&node, &qry_nuc_subs, &qry_missing, &aln_range, &masked_ranges);
let qry_deletions: Vec<NucDelRange> = vec![];
let result = tree_calculate_node_distance(
&node,
&qry_nuc_subs,
&qry_missing,
&qry_deletions,
&aln_range,
&masked_ranges,
);

assert_eq!(result, 0);

Expand All @@ -343,7 +411,15 @@ mod tests {
NucRefGlobalRange::from_usize(30, 50),
];

let result = tree_calculate_node_distance(&node, &qry_nuc_subs, &qry_missing, &aln_range, &masked_ranges);
let qry_deletions: Vec<NucDelRange> = vec![];
let result = tree_calculate_node_distance(
&node,
&qry_nuc_subs,
&qry_missing,
&qry_deletions,
&aln_range,
&masked_ranges,
);

assert_eq!(result, 3);

Expand All @@ -358,10 +434,51 @@ mod tests {
let aln_range = NucRefGlobalRange::from_usize(0, 30);
let masked_ranges = vec![NucRefGlobalRange::from_usize(12, 13)];

let result = tree_calculate_node_distance(&node, &qry_nuc_subs, &qry_missing, &aln_range, &masked_ranges);
let qry_deletions: Vec<NucDelRange> = vec![];
let result = tree_calculate_node_distance(
&node,
&qry_nuc_subs,
&qry_missing,
&qry_deletions,
&aln_range,
&masked_ranges,
);

assert_eq!(result, 3);

Ok(())
}

#[rstest]
fn deletion_covering_node_mutations_reduces_distance() -> Result<(), Report> {
let node = node_with_simple_nuc_subs();
let qry_nuc_subs: Vec<NucSub> = vec![];
let qry_missing: Vec<NucRange> = vec![];
let aln_range = NucRefGlobalRange::from_usize(0, 100);
let masked_ranges = vec![];

let no_deletions: Vec<NucDelRange> = vec![];
let distance_without_del = tree_calculate_node_distance(
&node,
&qry_nuc_subs,
&qry_missing,
&no_deletions,
&aln_range,
&masked_ranges,
);
assert_eq!(distance_without_del, 5);

let qry_deletions = vec![NucDelRange::from_usize(10, 25)];
let distance_with_del = tree_calculate_node_distance(
&node,
&qry_nuc_subs,
&qry_missing,
&qry_deletions,
&aln_range,
&masked_ranges,
);
assert_eq!(distance_with_del, 2);

Ok(())
}
}
Comment on lines +451 to 484
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests that adding a del reduces distance metric

Loading