From 8bbb9b307072ade7c04cc76cafa99614478f61f7 Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Mon, 15 Dec 2025 11:27:17 +0200 Subject: [PATCH 1/2] Make closed and open list of A* generic. --- Cargo.toml | 2 + generic_a_star/Cargo.toml | 4 +- generic_a_star/src/closed_lists.rs | 29 +++ generic_a_star/src/comparator.rs | 4 +- generic_a_star/src/lib.rs | 181 ++++++++++++++---- generic_a_star/src/open_lists.rs | 27 +++ generic_a_star/src/reset.rs | 8 - lib_ts_chainalign/src/chain_align.rs | 2 +- lib_ts_chainalign/src/chain_align/chainer.rs | 4 +- .../src/chaining_lower_bounds/gap_affine.rs | 2 +- .../chaining_lower_bounds/gap_affine/algo.rs | 4 +- .../src/exact_chaining/gap_affine.rs | 2 +- .../src/exact_chaining/gap_affine/algo.rs | 4 +- .../src/exact_chaining/ts_12_jump.rs | 2 +- .../src/exact_chaining/ts_12_jump/algo.rs | 4 +- .../src/exact_chaining/ts_34_jump.rs | 2 +- .../src/exact_chaining/ts_34_jump/algo.rs | 4 +- lib_tsalign/src/a_star_aligner.rs | 2 +- .../gap_affine_edit_distance.rs | 4 +- .../template_switch_distance/identifier.rs | 5 + .../lower_bounds/template_switch.rs | 41 ++-- .../lower_bounds/template_switch_alignment.rs | 2 +- seed_chain/src/chain.rs | 2 +- seed_chain/src/chain/node.rs | 4 +- 24 files changed, 262 insertions(+), 83 deletions(-) create mode 100644 generic_a_star/src/closed_lists.rs create mode 100644 generic_a_star/src/open_lists.rs diff --git a/Cargo.toml b/Cargo.toml index 03cb19db..35cc994a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,8 @@ compact-genome = "12.5.0" traitsequence = "8.1.2" log = "0.4.27" num-traits = "0.2.19" +rustc-hash = "2.1.1" +binary-heap-plus = "0.5.0" [profile.release] debug = true diff --git a/generic_a_star/Cargo.toml b/generic_a_star/Cargo.toml index b62a4f67..73bb6db1 100644 --- a/generic_a_star/Cargo.toml +++ b/generic_a_star/Cargo.toml @@ -11,9 +11,9 @@ repository.workspace = true serde = ["dep:serde"] [dependencies] -binary-heap-plus = "0.5.0" +binary-heap-plus.workspace = true num-traits.workspace = true serde = { workspace = true, features = ["derive"], optional = true } extend_map = "0.14.4" compare = "0.1.0" -rustc-hash = "2.1.1" +rustc-hash.workspace = true diff --git a/generic_a_star/src/closed_lists.rs b/generic_a_star/src/closed_lists.rs new file mode 100644 index 00000000..a43b024c --- /dev/null +++ b/generic_a_star/src/closed_lists.rs @@ -0,0 +1,29 @@ +use rustc_hash::{FxHashMapSeed, FxSeededState}; + +use crate::{AStarClosedList, AStarIdentifier, AStarNode, reset::Reset}; + +impl AStarClosedList + for FxHashMapSeed +{ + fn new() -> Self { + FxHashMapSeed::with_hasher(FxSeededState::with_seed(0)) + } + + fn len(&self) -> usize { + Self::len(self) + } + + fn insert(&mut self, identifier: Identifier, node: Node) -> Option { + Self::insert(self, identifier, node) + } + + fn get(&self, identifier: &Identifier) -> Option<&Node> { + Self::get(self, identifier) + } +} + +impl Reset for FxHashMapSeed { + fn reset(&mut self) { + self.clear(); + } +} diff --git a/generic_a_star/src/comparator.rs b/generic_a_star/src/comparator.rs index 4b7e4a5b..38a250b2 100644 --- a/generic_a_star/src/comparator.rs +++ b/generic_a_star/src/comparator.rs @@ -22,7 +22,7 @@ mod tests { use compare::Compare; - use crate::{AStarNode, cost::U64Cost}; + use crate::{AStarIdentifier, AStarNode, cost::U64Cost}; use super::AStarNodeComparator; @@ -101,6 +101,8 @@ mod tests { } } + impl AStarIdentifier for () {} + #[test] fn compare() { // Heap is a max heap, hence smaller nodes need to be bigger. diff --git a/generic_a_star/src/lib.rs b/generic_a_star/src/lib.rs index 58faef6a..f38fb02c 100644 --- a/generic_a_star/src/lib.rs +++ b/generic_a_star/src/lib.rs @@ -4,6 +4,7 @@ use std::{ cmp::Ordering, fmt::{Debug, Display}, hash::Hash, + marker::PhantomData, }; use binary_heap_plus::BinaryHeap; @@ -13,12 +14,17 @@ use cost::AStarCost; use extend_map::ExtendFilter; use num_traits::{Bounded, Zero}; use reset::Reset; -use rustc_hash::{FxHashMapSeed, FxSeededState}; +use rustc_hash::FxHashMapSeed; -mod comparator; +pub mod closed_lists; +pub mod comparator; pub mod cost; +pub mod open_lists; pub mod reset; +/// An identifier for nodes in the A* graph. +pub trait AStarIdentifier: Debug + Clone + Eq + Hash {} + /// A node of the A* graph. /// The node must implement [`Ord`], ordering it by its cost plus A* cost, ascending. /// The graph defined by the node type must be cycle-free. @@ -26,7 +32,7 @@ pub trait AStarNode: Sized + Ord + Debug + Display { /// A unique identifier of the node. /// /// For example, in case of traditional edit distance, this would be the tuple (i, j) indicating which alignment matrix cell this node belongs to. - type Identifier: Debug + Clone + Eq + Hash; + type Identifier: AStarIdentifier; /// The type collecting possible edge types. /// @@ -101,10 +107,59 @@ pub trait AStarContext: Reset { } /// The closed list for the A* algorithm. -pub trait AStarClosedList: Reset {} +pub trait AStarClosedList: Reset { + /// Create a new empty closed list. + fn new() -> Self; + + /// Returns the number of nodes in the closed list. + fn len(&self) -> usize; + + /// Returns true if the closed list is empty. + fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Insert a node with the given identifier into the closed list. + /// + /// If there was a node previously mapped to this identifier, then it is returned. + /// Otherwise, `None` is returned. + fn insert(&mut self, identifier: Identifier, node: Node) -> Option; + + /// Return a reference to the node specified by `identifier`. + /// + /// Returns `None` if no node with the given identifier exists. + fn get(&self, identifier: &Identifier) -> Option<&Node>; + + /// Returns true if the given identifier is mapped to a node. + fn contains_identifier(&self, identifier: &Identifier) -> bool { + self.get(identifier).is_some() + } +} /// The open list for the A* algorithm. -pub trait AStarOpenList: Reset {} +/// +/// This is a priority queue that must sort nodes with [`AStarNodeComparator`]. +// TODO is it enough to store cost + identifier? +pub trait AStarOpenList: Reset + Extend { + /// Create a new empty open list. + fn new() -> Self; + + /// Returns the number of nodes in the open list. + fn len(&self) -> usize; + + /// Returns true if the open list is empty. + fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Add the given node to the priority queue. + fn push(&mut self, node: Node); + + /// Removes and returns a minimum element from the priority queue. + /// + /// Returns `None` if the priority queue is empty. + fn pop_min(&mut self) -> Option; +} #[derive(Debug, Default)] pub struct AStarPerformanceCounters { @@ -129,18 +184,28 @@ pub enum AStarState { } #[derive(Debug)] -pub struct AStar { +pub struct AStar< + Context: AStarContext, + ClosedList: AStarClosedList<::Identifier, Context::Node> = FxHashMapSeed<<::Node as AStarNode>::Identifier, ::Node>, + OpenList: AStarOpenList = BinaryHeap<::Node, AStarNodeComparator>, +> { state: AStarState<::Identifier, ::Cost>, context: Context, - closed_list: FxHashMapSeed<::Identifier, Context::Node>, - open_list: BinaryHeap, + closed_list: ClosedList, + open_list: OpenList, performance_counters: AStarPerformanceCounters, } #[derive(Debug)] -pub struct AStarBuffers { - closed_list: FxHashMapSeed, - open_list: BinaryHeap, +pub struct AStarBuffers< + Identifier: AStarIdentifier, + Node: AStarNode, + ClosedList: AStarClosedList = FxHashMapSeed, + OpenList: AStarOpenList = BinaryHeap, +> { + closed_list: ClosedList, + open_list: OpenList, + phantom_data: PhantomData<(Identifier, Node)>, } #[derive(Debug, Clone, Ord, PartialOrd, PartialEq, Eq, Hash, Default)] @@ -168,33 +233,53 @@ pub enum AStarResult { NoTarget, } -struct BacktrackingIterator<'a_star, Context: AStarContext> { - a_star: &'a_star AStar, +struct BacktrackingIterator< + 'a_star, + Context: AStarContext, + ClosedList: AStarClosedList<::Identifier, Context::Node>, + OpenList: AStarOpenList, +> { + a_star: &'a_star AStar, current: ::Identifier, } -struct BacktrackingIteratorWithCost<'a_star, Context: AStarContext> { - a_star: &'a_star AStar, +struct BacktrackingIteratorWithCost< + 'a_star, + Context: AStarContext, + ClosedList: AStarClosedList<::Identifier, Context::Node>, + OpenList: AStarOpenList, +> { + a_star: &'a_star AStar, current: ::Identifier, } -impl AStar { +impl< + Context: AStarContext, + ClosedList: AStarClosedList<::Identifier, Context::Node>, + OpenList: AStarOpenList, +> AStar +{ pub fn new(context: Context) -> Self { Self { state: AStarState::Empty, context, - closed_list: FxHashMapSeed::with_hasher(FxSeededState::with_seed(0)), - open_list: BinaryHeap::from_vec(Vec::new()), + closed_list: ClosedList::new(), + open_list: OpenList::new(), performance_counters: Default::default(), } } pub fn new_with_buffers( context: Context, - mut buffers: AStarBuffers<::Identifier, Context::Node>, + mut buffers: AStarBuffers< + ::Identifier, + Context::Node, + ClosedList, + OpenList, + >, ) -> Self { - buffers.closed_list.clear(); - buffers.open_list.clear(); + buffers.closed_list.reset(); + buffers.open_list.reset(); Self { state: AStarState::Empty, context, @@ -225,10 +310,12 @@ impl AStar { pub fn into_buffers( self, - ) -> AStarBuffers<::Identifier, Context::Node> { + ) -> AStarBuffers<::Identifier, Context::Node, ClosedList, OpenList> + { AStarBuffers { closed_list: self.closed_list, open_list: self.open_list, + phantom_data: Default::default(), } } @@ -246,8 +333,8 @@ impl AStar { pub fn reset(&mut self) { self.state = AStarState::Empty; self.context.reset(); - self.closed_list.clear(); - self.open_list.clear(); + self.closed_list.reset(); + self.open_list.reset(); self.performance_counters = Default::default(); } @@ -301,7 +388,7 @@ impl AStar { let mut target_secondary_maximisable_score = 0; loop { - let Some(node) = self.open_list.pop() else { + let Some(node) = self.open_list.pop_min() else { if last_node.is_none() { unreachable!("Open list was empty."); }; @@ -447,7 +534,8 @@ impl AStar { pub fn backtrack( &self, - ) -> impl use<'_, Context> + Iterator::EdgeType> { + ) -> impl use<'_, Context, ClosedList, OpenList> + + Iterator::EdgeType> { let AStarState::Terminated { result: AStarResult::FoundTarget { identifier, .. }, } = &self.state @@ -464,7 +552,7 @@ impl AStar { /// The cost of the first node is never returned. pub fn backtrack_with_costs( &self, - ) -> impl use<'_, Context> + ) -> impl use<'_, Context, ClosedList, OpenList> + Iterator< Item = ( ::EdgeType, @@ -484,9 +572,11 @@ impl AStar { pub fn backtrack_from( &self, identifier: &::Identifier, - ) -> Option + Iterator::EdgeType>> - { - if self.closed_list.contains_key(identifier) { + ) -> Option< + impl use<'_, Context, ClosedList, OpenList> + + Iterator::EdgeType>, + > { + if self.closed_list.contains_identifier(identifier) { Some(BacktrackingIterator { a_star: self, current: identifier.clone(), @@ -505,7 +595,7 @@ impl AStar { &self, identifier: &::Identifier, ) -> Option< - impl use<'_, Context> + impl use<'_, Context, ClosedList, OpenList> + Iterator< Item = ( ::EdgeType, @@ -513,7 +603,7 @@ impl AStar { ), >, > { - if self.closed_list.contains_key(identifier) { + if self.closed_list.contains_identifier(identifier) { Some(BacktrackingIteratorWithCost { a_star: self, current: identifier.clone(), @@ -585,7 +675,12 @@ impl AStarResult { } } -impl Iterator for BacktrackingIterator<'_, Context> { +impl< + Context: AStarContext, + ClosedList: AStarClosedList<::Identifier, Context::Node>, + OpenList: AStarOpenList, +> Iterator for BacktrackingIterator<'_, Context, ClosedList, OpenList> +{ type Item = ::EdgeType; fn next(&mut self) -> Option { @@ -601,7 +696,12 @@ impl Iterator for BacktrackingIterator<'_, Context> { } } -impl Iterator for BacktrackingIteratorWithCost<'_, Context> { +impl< + Context: AStarContext, + ClosedList: AStarClosedList<::Identifier, Context::Node>, + OpenList: AStarOpenList, +> Iterator for BacktrackingIteratorWithCost<'_, Context, ClosedList, OpenList> +{ type Item = ( ::EdgeType, ::Cost, @@ -621,11 +721,18 @@ impl Iterator for BacktrackingIteratorWithCost<'_, Contex } } -impl Default for AStarBuffers { +impl< + Identifier: AStarIdentifier, + Node: AStarNode, + ClosedList: AStarClosedList, + OpenList: AStarOpenList, +> Default for AStarBuffers +{ fn default() -> Self { Self { - closed_list: FxHashMapSeed::with_hasher(FxSeededState::with_seed(0)), - open_list: BinaryHeap::from_vec(Vec::new()), + closed_list: ClosedList::new(), + open_list: OpenList::new(), + phantom_data: Default::default(), } } } diff --git a/generic_a_star/src/open_lists.rs b/generic_a_star/src/open_lists.rs new file mode 100644 index 00000000..b5f0c87f --- /dev/null +++ b/generic_a_star/src/open_lists.rs @@ -0,0 +1,27 @@ +use binary_heap_plus::BinaryHeap; + +use crate::{AStarNode, AStarOpenList, comparator::AStarNodeComparator, reset::Reset}; + +impl AStarOpenList for BinaryHeap { + fn new() -> Self { + BinaryHeap::from_vec(Vec::new()) + } + + fn len(&self) -> usize { + Self::len(self) + } + + fn push(&mut self, node: Node) { + Self::push(self, node); + } + + fn pop_min(&mut self) -> Option { + Self::pop(self) + } +} + +impl Reset for BinaryHeap { + fn reset(&mut self) { + self.clear(); + } +} diff --git a/generic_a_star/src/reset.rs b/generic_a_star/src/reset.rs index 324622f0..ec7d5d2a 100644 --- a/generic_a_star/src/reset.rs +++ b/generic_a_star/src/reset.rs @@ -1,5 +1,3 @@ -use rustc_hash::FxHashMapSeed; - pub trait Reset { fn reset(&mut self); } @@ -20,9 +18,3 @@ impl Reset for (A, B) { self.1.reset(); } } - -impl Reset for FxHashMapSeed { - fn reset(&mut self) { - self.clear(); - } -} diff --git a/lib_ts_chainalign/src/chain_align.rs b/lib_ts_chainalign/src/chain_align.rs index 35af6cde..bdb94a28 100644 --- a/lib_ts_chainalign/src/chain_align.rs +++ b/lib_ts_chainalign/src/chain_align.rs @@ -69,7 +69,7 @@ pub fn align( k, parameters.max_successors, ); - let mut astar = AStar::new(context); + let mut astar = AStar::<_>::new(context); let mut chaining_execution_count = 0; let mut current_lower_bound = Cost::zero(); let mut current_upper_bound = Cost::max_value(); diff --git a/lib_ts_chainalign/src/chain_align/chainer.rs b/lib_ts_chainalign/src/chain_align/chainer.rs index 1b744228..2605983e 100644 --- a/lib_ts_chainalign/src/chain_align/chainer.rs +++ b/lib_ts_chainalign/src/chain_align/chainer.rs @@ -1,6 +1,6 @@ use std::{fmt::Display, iter}; -use generic_a_star::{AStarContext, AStarNode, cost::AStarCost, reset::Reset}; +use generic_a_star::{AStarContext, AStarIdentifier, AStarNode, cost::AStarCost, reset::Reset}; use num_traits::Zero; use crate::{ @@ -558,6 +558,8 @@ impl AStarNode for Node { } } +impl AStarIdentifier for Identifier {} + impl Ord for Node { fn cmp(&self, other: &Self) -> std::cmp::Ordering { self.cost.cmp(&other.cost) diff --git a/lib_ts_chainalign/src/chaining_lower_bounds/gap_affine.rs b/lib_ts_chainalign/src/chaining_lower_bounds/gap_affine.rs index a6370e0e..6fc9ce34 100644 --- a/lib_ts_chainalign/src/chaining_lower_bounds/gap_affine.rs +++ b/lib_ts_chainalign/src/chaining_lower_bounds/gap_affine.rs @@ -39,7 +39,7 @@ impl GapAffineLowerBounds { LowerBoundCostArray::new_from_cost([max_n + 1, max_n + 1], Cost::max_value()); lower_bounds[[0, 0]] = Cost::zero(); let context = Context::new(cost_table, max_match_run, max_n); - let mut a_star = AStar::new(context); + let mut a_star = AStar::<_>::new(context); a_star.initialise(); a_star.search_until(|_, node| { if node.identifier.has_non_match || allow_all_match_run { diff --git a/lib_ts_chainalign/src/chaining_lower_bounds/gap_affine/algo.rs b/lib_ts_chainalign/src/chaining_lower_bounds/gap_affine/algo.rs index 1ff5e26d..ec39749f 100644 --- a/lib_ts_chainalign/src/chaining_lower_bounds/gap_affine/algo.rs +++ b/lib_ts_chainalign/src/chaining_lower_bounds/gap_affine/algo.rs @@ -1,7 +1,7 @@ use std::fmt::Display; use generic_a_star::{ - AStarContext, AStarNode, + AStarContext, AStarIdentifier, AStarNode, cost::{AStarCost, OrderedPairCost, U32Cost}, reset::Reset, }; @@ -224,3 +224,5 @@ impl Ord for Node { .then_with(|| self.match_run.cmp(&other.match_run)) } } + +impl AStarIdentifier for Identifier {} diff --git a/lib_ts_chainalign/src/exact_chaining/gap_affine.rs b/lib_ts_chainalign/src/exact_chaining/gap_affine.rs index bd6c7dd7..1f193362 100644 --- a/lib_ts_chainalign/src/exact_chaining/gap_affine.rs +++ b/lib_ts_chainalign/src/exact_chaining/gap_affine.rs @@ -31,7 +31,7 @@ impl GapAffineAlignment { ); let context = Context::new(cost_table, sequences, rc_fn, start, end, max_match_run); - let mut a_star = AStar::new(context); + let mut a_star = AStar::<_>::new(context); a_star.initialise(); match a_star.search() { AStarResult::FoundTarget { cost, .. } => Self { diff --git a/lib_ts_chainalign/src/exact_chaining/gap_affine/algo.rs b/lib_ts_chainalign/src/exact_chaining/gap_affine/algo.rs index 53d5c2b8..c480faf3 100644 --- a/lib_ts_chainalign/src/exact_chaining/gap_affine/algo.rs +++ b/lib_ts_chainalign/src/exact_chaining/gap_affine/algo.rs @@ -1,7 +1,7 @@ use std::fmt::Display; use generic_a_star::{ - AStarContext, AStarNode, + AStarContext, AStarIdentifier, AStarNode, cost::{AStarCost, OrderedPairCost, U32Cost}, reset::Reset, }; @@ -244,3 +244,5 @@ impl Ord for Node { .then_with(|| self.match_run.cmp(&other.match_run)) } } + +impl AStarIdentifier for Identifier {} diff --git a/lib_ts_chainalign/src/exact_chaining/ts_12_jump.rs b/lib_ts_chainalign/src/exact_chaining/ts_12_jump.rs index be130243..2c7765c5 100644 --- a/lib_ts_chainalign/src/exact_chaining/ts_12_jump.rs +++ b/lib_ts_chainalign/src/exact_chaining/ts_12_jump.rs @@ -30,7 +30,7 @@ impl Ts12JumpAlignment { assert!(end.is_secondary()); let context = Context::new(alignment_costs, sequences, rc_fn, start, end, max_match_run); - let mut a_star = AStar::new(context); + let mut a_star = AStar::<_>::new(context); a_star.initialise(); match a_star.search() { AStarResult::FoundTarget { cost, .. } => Self { diff --git a/lib_ts_chainalign/src/exact_chaining/ts_12_jump/algo.rs b/lib_ts_chainalign/src/exact_chaining/ts_12_jump/algo.rs index edbf686c..cef1a7cb 100644 --- a/lib_ts_chainalign/src/exact_chaining/ts_12_jump/algo.rs +++ b/lib_ts_chainalign/src/exact_chaining/ts_12_jump/algo.rs @@ -1,7 +1,7 @@ use std::fmt::Display; use generic_a_star::{ - AStarContext, AStarNode, + AStarContext, AStarIdentifier, AStarNode, cost::{AStarCost, OrderedPairCost, U32Cost}, reset::Reset, }; @@ -340,3 +340,5 @@ impl Ord for Node { .then_with(|| self.match_run.cmp(&other.match_run)) } } + +impl AStarIdentifier for Identifier {} diff --git a/lib_ts_chainalign/src/exact_chaining/ts_34_jump.rs b/lib_ts_chainalign/src/exact_chaining/ts_34_jump.rs index ecaa3fe9..16706cbe 100644 --- a/lib_ts_chainalign/src/exact_chaining/ts_34_jump.rs +++ b/lib_ts_chainalign/src/exact_chaining/ts_34_jump.rs @@ -30,7 +30,7 @@ impl Ts34JumpAlignment { assert!(end.is_primary()); let context = Context::new(alignment_costs, sequences, rc_fn, start, end, max_match_run); - let mut a_star = AStar::new(context); + let mut a_star = AStar::<_>::new(context); a_star.initialise(); match a_star.search() { AStarResult::FoundTarget { cost, .. } => Self { diff --git a/lib_ts_chainalign/src/exact_chaining/ts_34_jump/algo.rs b/lib_ts_chainalign/src/exact_chaining/ts_34_jump/algo.rs index c33193a6..c8ab2d55 100644 --- a/lib_ts_chainalign/src/exact_chaining/ts_34_jump/algo.rs +++ b/lib_ts_chainalign/src/exact_chaining/ts_34_jump/algo.rs @@ -1,7 +1,7 @@ use std::fmt::Display; use generic_a_star::{ - AStarContext, AStarNode, + AStarContext, AStarIdentifier, AStarNode, cost::{AStarCost, OrderedPairCost, U32Cost}, reset::Reset, }; @@ -349,3 +349,5 @@ impl Ord for Node { .then_with(|| self.match_run.cmp(&other.match_run)) } } + +impl AStarIdentifier for Identifier {} diff --git a/lib_tsalign/src/a_star_aligner.rs b/lib_tsalign/src/a_star_aligner.rs index 69c5531c..a22cf32d 100644 --- a/lib_tsalign/src/a_star_aligner.rs +++ b/lib_tsalign/src/a_star_aligner.rs @@ -58,7 +58,7 @@ where let start_time = Instant::now(); // Perform forwards search. - let mut a_star = AStar::new(context); + let mut a_star = AStar::<_>::new(context); a_star.initialise(); let result = a_star.search(); let has_target = matches!(result, AStarResult::FoundTarget { .. }); diff --git a/lib_tsalign/src/a_star_aligner/gap_affine_edit_distance.rs b/lib_tsalign/src/a_star_aligner/gap_affine_edit_distance.rs index fc6d7f63..2a9c1dd2 100644 --- a/lib_tsalign/src/a_star_aligner/gap_affine_edit_distance.rs +++ b/lib_tsalign/src/a_star_aligner/gap_affine_edit_distance.rs @@ -1,7 +1,7 @@ use std::marker::PhantomData; use compact_genome::interface::{alphabet::Alphabet, sequence::GenomeSequence}; -use generic_a_star::{AStarContext, AStarNode, cost::AStarCost, reset::Reset}; +use generic_a_star::{AStarContext, AStarIdentifier, AStarNode, cost::AStarCost, reset::Reset}; use super::{ AlignmentContext, alignment_geometry::AlignmentRange, alignment_result::IAlignmentType, @@ -386,3 +386,5 @@ impl IAlignmentType for AlignmentType { false } } + +impl AStarIdentifier for Identifier {} diff --git a/lib_tsalign/src/a_star_aligner/template_switch_distance/identifier.rs b/lib_tsalign/src/a_star_aligner/template_switch_distance/identifier.rs index 98a62fd7..cdcdba7c 100644 --- a/lib_tsalign/src/a_star_aligner/template_switch_distance/identifier.rs +++ b/lib_tsalign/src/a_star_aligner/template_switch_distance/identifier.rs @@ -1,4 +1,7 @@ +use std::{fmt::Debug, hash::Hash}; + use compact_genome::interface::{alphabet::Alphabet, sequence::GenomeSequence}; +use generic_a_star::AStarIdentifier; use super::{ AlignmentType, Context, @@ -495,3 +498,5 @@ impl TemplateSwitchDirection { *self } } + +impl AStarIdentifier for Identifier {} diff --git a/lib_tsalign/src/a_star_aligner/template_switch_distance/lower_bounds/template_switch.rs b/lib_tsalign/src/a_star_aligner/template_switch_distance/lower_bounds/template_switch.rs index 52cdbee0..0a72148e 100644 --- a/lib_tsalign/src/a_star_aligner/template_switch_distance/lower_bounds/template_switch.rs +++ b/lib_tsalign/src/a_star_aligner/template_switch_distance/lower_bounds/template_switch.rs @@ -89,26 +89,27 @@ impl TemplateSwitchLowerBoundMatrix { AlphabetType::iter().next().unwrap(), genome_length, )); - let mut a_star = AStar::new( - Context::<_, TSLBAlignmentStrategies>::new( - genome.as_genome_subsequence(), - genome.as_genome_subsequence(), - "", - "", - AlignmentRange::new_complete(genome.len(), genome.len()), - lower_bound_config.clone(), - Memory { - template_switch_min_length: (), - chaining: (), - template_switch_count: 1, - shortcut: (), - primary_match: (), - }, - None, - None, - false, - ), - ); + let mut a_star = AStar::<_>::new(Context::< + _, + TSLBAlignmentStrategies, + >::new( + genome.as_genome_subsequence(), + genome.as_genome_subsequence(), + "", + "", + AlignmentRange::new_complete(genome.len(), genome.len()), + lower_bound_config.clone(), + Memory { + template_switch_min_length: (), + chaining: (), + template_switch_count: 1, + shortcut: (), + primary_match: (), + }, + None, + None, + false, + )); let root_xy = genome_length / 2; a_star.initialise_with(|context| Node::new_root_at(root_xy, root_xy, context).into()); previous_closed_lower_bounds.extend(closed_lower_bounds.drain()); diff --git a/lib_tsalign/src/a_star_aligner/template_switch_distance/lower_bounds/template_switch_alignment.rs b/lib_tsalign/src/a_star_aligner/template_switch_distance/lower_bounds/template_switch_alignment.rs index ee1877f2..8768b491 100644 --- a/lib_tsalign/src/a_star_aligner/template_switch_distance/lower_bounds/template_switch_alignment.rs +++ b/lib_tsalign/src/a_star_aligner/template_switch_distance/lower_bounds/template_switch_alignment.rs @@ -91,7 +91,7 @@ impl TemplateSwitchAlignmentLowerBoundMatrix { AlphabetType::iter().next().unwrap(), genome_length, )); - let mut a_star = AStar::new( + let mut a_star = AStar::<_>::new( Context::<_, TSALBAlignmentStrategies>::new( genome.as_genome_subsequence(), genome.as_genome_subsequence(), diff --git a/seed_chain/src/chain.rs b/seed_chain/src/chain.rs index 922e8202..5af52c49 100644 --- a/seed_chain/src/chain.rs +++ b/seed_chain/src/chain.rs @@ -27,7 +27,7 @@ impl Chain { chaining_anchors: ChainingAnchors, ) -> Self { info!("Computing chain..."); - let mut a_star = AStar::new(Context::new(chaining_costs, chaining_anchors)); + let mut a_star = AStar::<_>::new(Context::new(chaining_costs, chaining_anchors)); a_star.initialise(); a_star.search(); diff --git a/seed_chain/src/chain/node.rs b/seed_chain/src/chain/node.rs index 2111cc09..0dffc25a 100644 --- a/seed_chain/src/chain/node.rs +++ b/seed_chain/src/chain/node.rs @@ -1,4 +1,4 @@ -use generic_a_star::{AStarNode, cost::AStarCost}; +use generic_a_star::{AStarIdentifier, AStarNode, cost::AStarCost}; use crate::seed::ChainingAnchor; @@ -99,3 +99,5 @@ impl PartialOrd for Node { Some(self.cmp(other)) } } + +impl AStarIdentifier for Identifier {} From 9112e9303ded8583b62d581d6a78f14e37f3b6a8 Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Mon, 15 Dec 2025 11:34:50 +0200 Subject: [PATCH 2/2] Remove wrong TODO. --- generic_a_star/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/generic_a_star/src/lib.rs b/generic_a_star/src/lib.rs index f38fb02c..6bc0b461 100644 --- a/generic_a_star/src/lib.rs +++ b/generic_a_star/src/lib.rs @@ -139,7 +139,6 @@ pub trait AStarClosedList: Reset { /// The open list for the A* algorithm. /// /// This is a priority queue that must sort nodes with [`AStarNodeComparator`]. -// TODO is it enough to store cost + identifier? pub trait AStarOpenList: Reset + Extend { /// Create a new empty open list. fn new() -> Self;