Processing state of graph nodes
In most case, we have to maintain some form of processing state while we perform a graph search. The most common processing data that we need to calculate and store is
- A node's visiting
state
- The
parent
node, that is, the node we are visiting from - A unit in terms of
cost
ordistance
The Tracker
structure holds the processed graph information while provides the means to
- access a node as an index in the form of
tracker[node]
- set and update the path cost to a specific node
- set and update the parent node for the path cost calculation
- extract the minimum cost path given a target node The below code implements the above functionality
#[derive(Debug,Copy,Clone,PartialEq)]
pub enum NodeState {
Undiscovered,
Discovered,
Processed
}
#[derive(Debug,Clone)]
pub struct NodeTrack {
visited:NodeState,
dist:Cost,
parent:Option<Node>
}
impl NodeTrack {
pub fn visited(&mut self, s:NodeState) -> &mut Self {
self.visited = s; self
}
pub fn distance(&mut self, d:Cost) -> &mut Self {
self.dist = d; self
}
pub fn parent(&mut self, n:Node) -> &mut Self {
self.parent =Some(n); self
}
pub fn is_discovered(&self) -> bool {
self.visited != NodeState::Undiscovered
}
}
#[derive(Debug)]
pub struct Tracker {
list: HashMap<Node, NodeTrack>
}
pub trait Tracking {
fn extract(&self, start:Node) -> (Vec<Node>, Cost) {
(self.extract_path(start), self.extract_cost(start))
}
fn extract_path(&self, start: Node) -> Vec<Node>;
fn extract_cost(&self, start: Node) -> Cost;
}
impl Tracking for Tracker {
fn extract_path(&self, start:Node) -> Vec<Node> {
let mut path = VecDeque::new();
// reconstruct the shortest path starting from the target node
path.push_front(start);
// set target as current node
let mut cur_node= start;
// backtrace all parents until you reach None, that is, the start node
while let Some(parent) = self[cur_node].parent {
path.push_front(parent);
cur_node = parent;
}
path.into()
}
fn extract_cost(&self, start:Node) -> Cost {
self[start].dist
}
}
impl Index<Node> for Tracker {
type Output = NodeTrack;
fn index(&self, index: Node) -> &Self::Output {
self.list.get(&index).unwrap_or_else(|| panic!("Error: cannot find {index} in tracker {:?}", &self))
}
}
impl IndexMut<Node> for Tracker {
fn index_mut(&mut self, index: Node) -> &mut Self::Output {
self.list.get_mut(&index).unwrap_or_else(|| panic!("Error: cannot find {index} in tracker"))
}
}
To initialise the Tracker
we use the Graph
structure
impl Graph {
pub fn get_tracker(&self, visited: NodeState, dist: Cost, parent: Option<Node>) -> Tracker {
Tracker {
list: self.nodes.iter()
.fold(HashMap::new(), |mut out, &node| {
out.entry(node)
.or_insert(NodeTrack { visited, dist, parent });
out
})
}
}
}