use crate::core::error::{GraphinaError, Result};
use crate::core::types::{BaseGraph, GraphConstructor, NodeId, NodeMap, NodeSet};
use petgraph::visit::NodeIndexable;
use std::collections::VecDeque;
pub fn bfs<A, W, Ty>(graph: &BaseGraph<A, W, Ty>, start: NodeId) -> Vec<NodeId>
where
Ty: GraphConstructor<A, W>,
{
if graph.node_attr(start).is_none() {
return Vec::new();
}
let mut visited = vec![false; graph.as_petgraph().node_bound()];
let mut order = Vec::new();
let mut queue = VecDeque::new();
visited[start.index()] = true;
queue.push_back(start);
while let Some(node) = queue.pop_front() {
order.push(node);
for neighbor in graph.neighbors(node) {
let ni = neighbor.index();
if !visited[ni] {
visited[ni] = true;
queue.push_back(neighbor);
}
}
}
order
}
pub fn dfs<A, W, Ty>(graph: &BaseGraph<A, W, Ty>, start: NodeId) -> Vec<NodeId>
where
Ty: GraphConstructor<A, W>,
{
if graph.node_attr(start).is_none() {
return Vec::new();
}
let mut visited = vec![false; graph.as_petgraph().node_bound()];
let mut order = Vec::new();
dfs_util(graph, start, &mut visited, &mut order);
order
}
fn dfs_util<A, W, Ty>(
graph: &BaseGraph<A, W, Ty>,
node: NodeId,
visited: &mut [bool],
order: &mut Vec<NodeId>,
) where
Ty: GraphConstructor<A, W>,
{
if visited[node.index()] {
return;
}
visited[node.index()] = true;
order.push(node);
for neighbor in graph.neighbors(node) {
if !visited[neighbor.index()] {
dfs_util(graph, neighbor, visited, order);
}
}
}
pub fn iddfs<A, W, Ty>(
graph: &BaseGraph<A, W, Ty>,
start: NodeId,
target: NodeId,
max_depth: usize,
) -> Option<Vec<NodeId>>
where
Ty: GraphConstructor<A, W>,
{
for depth in 0..=max_depth {
let mut path = Vec::new();
let mut visited = NodeSet::default();
if dls(graph, start, target, depth, &mut visited, &mut path) {
return Some(path);
}
}
None
}
pub fn try_iddfs<A, W, Ty>(
graph: &BaseGraph<A, W, Ty>,
start: NodeId,
target: NodeId,
max_depth: usize,
) -> Result<Vec<NodeId>>
where
Ty: GraphConstructor<A, W>,
{
if graph.node_attr(start).is_none() || graph.node_attr(target).is_none() {
return Err(GraphinaError::node_not_found(
"Start or target node not found",
));
}
for depth in 0..=max_depth {
let mut path = Vec::new();
let mut visited = NodeSet::default();
if dls(graph, start, target, depth, &mut visited, &mut path) {
return Ok(path);
}
}
Err(GraphinaError::no_path(format!(
"No path found from {} to {} within depth {}",
start.index(),
target.index(),
max_depth
)))
}
fn dls<A, W, Ty>(
graph: &BaseGraph<A, W, Ty>,
current: NodeId,
target: NodeId,
depth: usize,
visited: &mut NodeSet,
path: &mut Vec<NodeId>,
) -> bool
where
Ty: GraphConstructor<A, W>,
{
path.push(current);
visited.insert(current);
if current == target {
return true;
}
if depth > 0 {
for neighbor in graph.neighbors(current) {
if !visited.contains(&neighbor)
&& dls(graph, neighbor, target, depth - 1, visited, path)
{
return true;
}
}
}
path.pop();
visited.remove(¤t);
false
}
pub fn bidis<A, W, Ty>(
graph: &BaseGraph<A, W, Ty>,
start: NodeId,
target: NodeId,
) -> Option<Vec<NodeId>>
where
Ty: GraphConstructor<A, W>,
{
if start == target {
return Some(vec![start]);
}
let mut forward_queue = VecDeque::new();
let mut backward_queue = VecDeque::new();
let mut forward_parents: NodeMap<Option<NodeId>> = NodeMap::default();
let mut backward_parents: NodeMap<Option<NodeId>> = NodeMap::default();
let mut forward_visited = NodeSet::default();
let mut backward_visited = NodeSet::default();
let mut forward_frontier = NodeSet::default();
let mut backward_frontier = NodeSet::default();
forward_queue.push_back(start);
forward_visited.insert(start);
forward_frontier.insert(start);
forward_parents.insert(start, None);
backward_queue.push_back(target);
backward_visited.insert(target);
backward_frontier.insert(target);
backward_parents.insert(target, None);
let mut meeting_node = None;
while !forward_queue.is_empty() && !backward_queue.is_empty() {
if let Some(&meet) = forward_frontier.intersection(&backward_frontier).next() {
meeting_node = Some(meet);
break;
}
forward_frontier.clear();
let forward_level = forward_queue.len();
for _ in 0..forward_level {
if let Some(current) = forward_queue.pop_front() {
for neighbor in graph.neighbors(current) {
if forward_visited.insert(neighbor) {
forward_queue.push_back(neighbor);
forward_frontier.insert(neighbor);
forward_parents.insert(neighbor, Some(current));
}
}
}
}
if let Some(&meet) = forward_frontier.intersection(&backward_visited).next() {
meeting_node = Some(meet);
break;
}
backward_frontier.clear();
let backward_level = backward_queue.len();
for _ in 0..backward_level {
if let Some(current) = backward_queue.pop_front() {
for neighbor in get_backward_neighbors(graph, current) {
if backward_visited.insert(neighbor) {
backward_queue.push_back(neighbor);
backward_frontier.insert(neighbor);
backward_parents.insert(neighbor, Some(current));
}
}
}
}
if let Some(&meet) = backward_frontier.intersection(&forward_visited).next() {
meeting_node = Some(meet);
break;
}
}
if let Some(meet) = meeting_node {
let mut forward_path = Vec::new();
let mut cur = meet;
while let Some(&Some(parent)) = forward_parents.get(&cur) {
forward_path.push(cur);
cur = parent;
}
forward_path.push(start);
forward_path.reverse();
let mut backward_path = Vec::new();
cur = meet;
while let Some(&Some(parent)) = backward_parents.get(&cur) {
backward_path.push(parent);
cur = parent;
}
let mut full_path = forward_path;
full_path.extend(backward_path);
return Some(full_path);
}
None
}
pub fn try_bidirectional_search<A, W, Ty>(
graph: &BaseGraph<A, W, Ty>,
start: NodeId,
target: NodeId,
) -> Result<Vec<NodeId>>
where
Ty: GraphConstructor<A, W>,
{
if graph.node_attr(start).is_none() || graph.node_attr(target).is_none() {
return Err(GraphinaError::node_not_found(
"Start or target node not found",
));
}
bidis(graph, start, target).ok_or_else(|| {
GraphinaError::no_path(format!(
"No path found from {} to {}",
start.index(),
target.index()
))
})
}
fn get_backward_neighbors<A, W, Ty>(graph: &BaseGraph<A, W, Ty>, node: NodeId) -> Vec<NodeId>
where
Ty: GraphConstructor<A, W>,
{
graph.incoming_neighbors(node).collect()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::types::Graph;
#[test]
fn test_bfs() {
let mut graph = Graph::<i32, ()>::new();
let n1 = graph.add_node(1);
let n2 = graph.add_node(2);
let n3 = graph.add_node(3);
graph.add_edge(n1, n2, ());
graph.add_edge(n2, n3, ());
let visited = bfs(&graph, n1);
assert_eq!(visited.len(), 3);
assert!(visited.contains(&n1));
assert!(visited.contains(&n2));
assert!(visited.contains(&n3));
}
#[test]
fn test_dfs() {
let mut graph = Graph::<i32, ()>::new();
let n1 = graph.add_node(1);
let n2 = graph.add_node(2);
let n3 = graph.add_node(3);
graph.add_edge(n1, n2, ());
graph.add_edge(n2, n3, ());
let visited = dfs(&graph, n1);
assert_eq!(visited.len(), 3);
assert!(visited.contains(&n1));
assert!(visited.contains(&n2));
assert!(visited.contains(&n3));
}
#[test]
fn test_bfs_excludes_unreachable_component() {
let mut graph = Graph::<i32, ()>::new();
let n1 = graph.add_node(1);
let n2 = graph.add_node(2);
let n3 = graph.add_node(3);
let n4 = graph.add_node(4);
let n5 = graph.add_node(5);
graph.add_edge(n1, n2, ());
graph.add_edge(n2, n3, ());
graph.add_edge(n4, n5, ());
let order = bfs(&graph, n1);
assert_eq!(order.len(), 3);
assert!(order.contains(&n1) && order.contains(&n2) && order.contains(&n3));
assert!(!order.contains(&n4) && !order.contains(&n5));
assert_eq!(order[0], n1);
}
#[test]
fn test_dfs_excludes_unreachable_component() {
let mut graph = Graph::<i32, ()>::new();
let n1 = graph.add_node(1);
let n2 = graph.add_node(2);
let n3 = graph.add_node(3);
let n4 = graph.add_node(4);
graph.add_edge(n1, n2, ());
graph.add_edge(n2, n3, ());
let order = dfs(&graph, n1);
assert_eq!(order.len(), 3);
assert!(!order.contains(&n4));
assert_eq!(order[0], n1);
}
#[test]
fn test_bfs_missing_start_is_empty() {
let mut graph = Graph::<i32, ()>::new();
let n1 = graph.add_node(1);
graph.remove_node(n1);
assert!(bfs(&graph, n1).is_empty());
assert!(dfs(&graph, n1).is_empty());
}
#[test]
fn test_bidis() {
let mut graph = Graph::<i32, ()>::new();
let n1 = graph.add_node(1);
let n2 = graph.add_node(2);
let n3 = graph.add_node(3);
graph.add_edge(n1, n2, ());
graph.add_edge(n2, n3, ());
let path = bidis(&graph, n1, n3);
assert!(path.is_some());
let path = path.unwrap();
assert_eq!(path[0], n1);
assert_eq!(path[path.len() - 1], n3);
}
}