From 37bc313f200a739765700ca8c85834ad38466ca6 Mon Sep 17 00:00:00 2001 From: Antonio De Lucreziis Date: Tue, 22 Oct 2024 21:04:48 +0200 Subject: [PATCH] mid thorugh refactoring --- examples/edge_classify.py | 103 +++++++++ examples/edge_classify_2.py | 68 ++++++ src/adv_graph.rs | 16 -- src/graph/algorithms.rs | 393 ++++++++++++++++++++++++++++++++++ src/graph/edge_types.rs | 114 ++++++++++ src/graph/mod.rs | 57 +++++ src/{graph.rs => graphhhh.rs} | 171 +++++---------- src/lib.rs | 1 - src/main.rs | 22 +- 9 files changed, 808 insertions(+), 137 deletions(-) create mode 100644 examples/edge_classify.py create mode 100644 examples/edge_classify_2.py delete mode 100644 src/adv_graph.rs create mode 100644 src/graph/algorithms.rs create mode 100644 src/graph/edge_types.rs create mode 100644 src/graph/mod.rs rename src/{graph.rs => graphhhh.rs} (68%) diff --git a/examples/edge_classify.py b/examples/edge_classify.py new file mode 100644 index 0000000..fb3c04a --- /dev/null +++ b/examples/edge_classify.py @@ -0,0 +1,103 @@ +# code +import random + + +class Graph: + # instance variables + def __init__(self, v): + # v is the number of nodes/vertices + self.time = 0 + self.traversal_array = [] + self.v = v + # e is the number of edge (randomly chosen between 9 to 45) + self.e = random.randint(9, 45) + # adj. list for graph + self.graph_list = [[] for _ in range(v)] + # adj. matrix for graph + self.graph_matrix = [[0 for _ in range(v)] for _ in range(v)] + + # function to create random graph + def create_random_graph(self): + # add edges upto e + for i in range(self.e): + # choose src and dest of each edge randomly + src = random.randrange(0, self.v) + dest = random.randrange(0, self.v) + # re-choose if src and dest are same or src and dest already has an edge + while src == dest and self.graph_matrix[src][dest] == 1: + src = random.randrange(0, self.v) + dest = random.randrange(0, self.v) + # add the edge to graph + self.add_edge(src, dest) + + def add_edge(self, src, dest): + self.graph_list[src].append(dest) + self.graph_matrix[src][dest] = 1 + + # function to print adj list + def print_graph_list(self): + print("Adjacency List Representation:") + for i in range(self.v): + print(i, "-->", *self.graph_list[i]) + print() + + # function to print adj matrix + def print_graph_matrix(self): + print("Adjacency Matrix Representation:") + for i in self.graph_matrix: + print(i) + print() + + # function the get number of edges + def number_of_edges(self): + return self.e + + # function for dfs + def dfs(self): + self.visited = [False]*self.v + self.start_time = [0]*self.v + self.end_time = [0]*self.v + + for node in range(self.v): + if not self.visited[node]: + self.traverse_dfs(node) + print() + print("DFS Traversal: ", self.traversal_array) + print() + + def traverse_dfs(self, node): + self.visited[node] = True + self.traversal_array.append(node) + self.start_time[node] = self.time + self.time += 1 + for neighbour in self.graph_list[node]: + print('Edge:', str(node)+'-->'+str(neighbour)) + + if not self.visited[neighbour]: + print(' => Tree Edge') + self.traverse_dfs(neighbour) + else: + print(f"Times: ({self.start_time[node]}, {self.end_time[node]}) ({self.start_time[neighbour]}, {self.end_time[neighbour]})") + + if self.start_time[node] > self.start_time[neighbour] and self.end_time[node] < self.end_time[neighbour]: + print(' => Back Edge') + elif self.start_time[node] < self.start_time[neighbour] and self.end_time[node] > self.end_time[neighbour]: + print(' => Forward Edge') + else: + print(' => Cross Edge') + self.end_time[node] = self.time + self.time += 1 + + +if __name__ == "__main__": + g = Graph(4) + # g.create_random_graph() + + g.add_edge(0, 1) + g.add_edge(1, 2) + g.add_edge(2, 3) + g.add_edge(3, 1) + + g.print_graph_list() + g.print_graph_matrix() + g.dfs() diff --git a/examples/edge_classify_2.py b/examples/edge_classify_2.py new file mode 100644 index 0000000..cabfcab --- /dev/null +++ b/examples/edge_classify_2.py @@ -0,0 +1,68 @@ +class DFSResult: + def __init__(self): + self.parent = {} + self.start_time = {} + self.finish_time = {} + self.edges = {} # Edge classification for directed graph. + self.order = [] + self.t = 0 + +def dfs(g): + results = DFSResult() + for vertex in g.vertices(): + if vertex not in results.parent: + dfs_visit(g, vertex, results) + return results + +def dfs_visit(g, v, results, parent=None): + results.parent[v] = parent + results.t += 1 + results.start_time[v] = results.t + if parent is not None: + results.edges[(parent, v)] = 'tree' + + for n in g.neighbors(v): + if n not in results.parent: # n is not visited + dfs_visit(g, n, results, v) + elif n not in results.finish_time: + results.edges[(v, n)] = 'back' + elif results.start_time[v] < results.start_time[n]: + results.edges[(v, n)] = 'forward' + else: + results.edges[(v, n)] = 'cross' + + results.t += 1 + results.finish_time[v] = results.t + results.order.append(v) + +# Graph structure +class Graph: + def __init__(self): + self.adjacency_list = {} + + def add_edge(self, u, v): + if u not in self.adjacency_list: + self.adjacency_list[u] = [] + self.adjacency_list[u].append(v) + + def vertices(self): + return self.adjacency_list.keys() + + def neighbors(self, v): + return self.adjacency_list.get(v, []) + +# Example usage: +g = Graph() +g.add_edge(0, 1) +g.add_edge(1, 2) +g.add_edge(2, 3) +g.add_edge(3, 0) # Creating the cycle 0 -> 1 -> 2 -> 3 -> 0 + +# Running DFS +results = dfs(g) + +print("Parent Map:", results.parent) +print("Start Times:", results.start_time) +print("Finish Times:", results.finish_time) +print("Edge Classifications:", results.edges) +print("DFS Order:", results.order) diff --git a/src/adv_graph.rs b/src/adv_graph.rs deleted file mode 100644 index 9264504..0000000 --- a/src/adv_graph.rs +++ /dev/null @@ -1,16 +0,0 @@ -use std::{collections::HashMap, hash::Hash}; - -struct GraphEdge { - from: u32, - to: u32, -} - -pub struct Graph -where - V: Hash + Eq + Clone, -{ - nodes: HashMap, - edges: HashMap<(u32, u32), E>, - - adjacency_list: HashMap>, -} diff --git a/src/graph/algorithms.rs b/src/graph/algorithms.rs new file mode 100644 index 0000000..2abc7ce --- /dev/null +++ b/src/graph/algorithms.rs @@ -0,0 +1,393 @@ +use std::{ + cell::RefCell, + collections::{BTreeMap, HashMap, HashSet, VecDeque}, + fmt::Debug, + hash::Hash, + rc::Rc, +}; + +use indicatif::ProgressIterator; + +use super::{AdjacencyGraph, UndirectedGraph}; + +#[allow(dead_code)] +impl AdjacencyGraph +where + V: Hash + Eq + Clone + Debug, +{ + pub fn new() -> Self { + AdjacencyGraph { + nodes: HashSet::new(), + adjacencies: HashMap::new(), + } + } + + pub fn add_node(&mut self, node: V) { + // O(1) + self.nodes.insert(node); + } + + pub fn add_edge(&mut self, from: V, to: V) { + // O(1) + self.add_node(from.clone()); + self.add_node(to.clone()); + + // O(1) + self.adjacencies + .entry(from) + .or_insert_with(HashSet::new) + .insert(to); + } + + pub fn get_adjacencies(&self, node: &V) -> Option<&HashSet> { + self.adjacencies.get(node) + } + + pub fn adjacencies(&self) -> &HashMap> { + &self.adjacencies + } + + pub fn nodes(&self) -> &HashSet { + &self.nodes + } + + pub fn edges(&self) -> impl Iterator { + self.adjacencies + .iter() + .flat_map(|(from, tos)| tos.iter().map(move |to| (from, to))) + } + + pub fn opposite(&self) -> AdjacencyGraph<&V> { + let mut opposite = AdjacencyGraph::new(); + + // O(|E|) + for (from, to) in self.edges() { + opposite.add_edge(to, from); + } + + opposite + } + + pub fn undirected(&self) -> UndirectedGraph<&V> { + let mut undirected = AdjacencyGraph::new(); + + // O(|E|) + for (from, to) in self.edges() { + undirected.add_edge(from, to); + undirected.add_edge(to, from); + } + + UndirectedGraph { graph: undirected } + } + + pub fn has_edge(&self, from: &V, to: &V) -> bool { + // O(1) + if let Some(adjacencies) = self.get_adjacencies(from) { + // O(1) + adjacencies.contains(&to.to_owned()) + } else { + false + } + } + + pub fn dfs<'a>(&'a self, node: &'a V) -> impl Iterator + 'a { + let mut visited = HashSet::new(); + let mut stack = VecDeque::from([node]); + + std::iter::from_fn(move || { + while let Some(node) = stack.pop_back() { + if !visited.insert(node.clone()) { + continue; + } + + if let Some(adjacencies) = self.get_adjacencies(node) { + stack.extend(adjacencies); + } + + return Some(node.clone()); + } + + None + }) + } + + /// This computes if this undirected graph is cyclic or not by searching for an oriented cycle in the graph + pub fn is_cyclic(&self) -> bool { + let mut remaining_nodes = self.nodes.iter().collect::>(); + + // let progress_bar = ProgressBar::new(self.nodes.len() as u64); + // let mut visited_count = 0; + + while !remaining_nodes.is_empty() { + let start: &V = remaining_nodes.iter().next().unwrap(); + + // visited_count += 1; + remaining_nodes.remove(start); + // progress_bar.inc(1); + + let mut dfs_visited = HashSet::new(); + let mut stack = VecDeque::new(); + stack.push_back(start); + + // start a new dfs from the current node + while let Some(node) = stack.pop_back() { + if dfs_visited.contains(node) { + // println!("Found cycle after {} nodes", visited_count); + // progress_bar.finish(); + return true; + } + + // visited_count += 1; + remaining_nodes.remove(node); + // progress_bar.inc(1); + + dfs_visited.insert(node.clone()); + + if let Some(adjacencies) = self.get_adjacencies(node) { + stack.extend(adjacencies); + } + } + } + + // println!("Found cycle after {} nodes", visited_count); + // progress_bar.finish(); + false + } + + pub fn shortest_path_matrix(&self) -> HashMap<&V, HashMap<&V, usize>> { + let mut result = HashMap::new(); + + for node in self.nodes.iter() { + let mut distances = HashMap::new(); + let mut visited = HashSet::new(); + let mut queue = VecDeque::from([node]); + + distances.insert(node, 0); + + while let Some(node) = queue.pop_front() { + if visited.contains(node) { + continue; + } + + visited.insert(node.clone()); + + let distance = *distances.get(node).unwrap(); + + if let Some(adjacencies) = self.get_adjacencies(node) { + for adj in adjacencies { + if !distances.contains_key(adj) { + distances.insert(adj, distance + 1); + queue.push_back(adj); + } + } + } + } + + result.insert(node, distances); + } + + result + } + + pub fn compute_ccs(&self) -> Vec> { + let mut visited = HashSet::new(); + let mut result = Vec::new(); + + let op = self.opposite(); + + for node in self + .nodes + .iter() + .progress() + .with_style( + indicatif::ProgressStyle::default_bar() + .template("{prefix} {spinner} [{elapsed_precise}] [{wide_bar}] {pos}/{len}") + .unwrap(), + ) + .with_prefix("computing connected components") + { + if visited.contains(node) { + continue; + } + + let mut cc: HashSet = HashSet::new(); + let mut stack: Vec<&V> = vec![node]; + + while let Some(node) = stack.pop() { + if cc.contains(node) { + continue; + } + + cc.insert(node.clone()); + + if let Some(adjacencies) = self.get_adjacencies(&node) { + for adj in adjacencies { + stack.push(adj); + } + } + + if let Some(adjacencies) = op.get_adjacencies(&node) { + for adj in adjacencies { + stack.push(adj); + } + } + } + + visited.extend(cc.iter().map(|x| x.to_owned())); + result.push(cc.iter().map(|x| x.to_owned()).collect()); + } + + result + } + + pub fn compute_ccs_2(&self) -> Vec> { + let mut cc: HashMap>>> = HashMap::new(); + + for node in self.nodes.iter() { + if cc.contains_key(&node) { + continue; + } + + // println!("All CC: {:?}", cc); + + let new_cc = Rc::new(RefCell::new(HashSet::new())); + + let mut stack: Vec<&V> = vec![node]; + + while let Some(node) = stack.pop() { + // println!("New CC: {:?}", new_cc.borrow()); + + if cc.contains_key(&node) { + // merge the two connected components and go to the next node + + let old_cc: &Rc>> = cc.get(&node).unwrap(); + + // println!( + // "Merging {:?} with {:?} due to link to {:?}", + // new_cc.borrow(), + // old_cc.borrow(), + // node + // ); + + new_cc + .borrow_mut() + .extend(old_cc.borrow().iter().map(|x| x.to_owned())); + + break; + } + + if new_cc.borrow().contains(&node) { + continue; + } + + new_cc.borrow_mut().insert(node.clone()); + + if let Some(adjacencies) = self.get_adjacencies(&node) { + for adj in adjacencies { + stack.push(adj); + } + } + } + + for n in new_cc.borrow().iter() { + cc.insert(n.to_owned(), new_cc.clone()); + } + } + + // extract the unique connected components by pointers + let mut result = Vec::new(); + let mut seen = HashSet::new(); + + for node in self.nodes.iter() { + if seen.contains(node) { + continue; + } + + let cc = cc.get(node).unwrap(); + seen.extend(cc.borrow().iter().map(|x| x.to_owned())); + + result.push(cc.borrow().iter().map(|x| x.to_owned()).collect()); + } + + result + } + + /// This function prints the number of nodes, edges and a histogram of the degrees of the nodes + /// in the graph (computing the degrees might take a long time) + pub fn print_stats(&self) { + let mut vertices_degrees = HashMap::new(); + + for (from, tos) in self + .adjacencies + .iter() + .progress() + .with_style( + indicatif::ProgressStyle::default_bar() + .template("{prefix} {spinner} [{elapsed_precise}] [{wide_bar}] {pos}/{len}") + .unwrap(), + ) + .with_prefix("computing nodes degrees") + { + *vertices_degrees.entry(from).or_insert(0) += tos.len(); + + for to in tos { + *vertices_degrees.entry(to).or_insert(0) += 1; + } + } + + let histogram: BTreeMap = vertices_degrees + .iter() + .map(|(_, degree)| *degree) + .fold(BTreeMap::new(), |mut acc, degree| { + *acc.entry(degree).or_insert(0) += 1; + acc + }); + + println!("Stats:"); + println!("Nodes: {}", self.nodes.len()); + println!("Edges: {}", self.edges().count()); + + println!("Histogram:"); + for (degree, count) in histogram.iter() { + println!("{}: {}", degree, count); + } + } +} + +impl UndirectedGraph +where + V: Hash + Eq + Clone + Debug, +{ + pub fn connected_components(&self) -> Vec> { + let mut visited = HashSet::new(); + let mut result = Vec::new(); + + for node in self.graph.nodes.iter() { + if visited.contains(node) { + continue; + } + + let mut cc: HashSet = HashSet::new(); + let mut stack: Vec<&V> = vec![node]; + + while let Some(node) = stack.pop() { + if cc.contains(node) { + continue; + } + + cc.insert(node.clone()); + + if let Some(adjacencies) = self.graph.get_adjacencies(&node) { + for adj in adjacencies { + stack.push(adj); + } + } + } + + visited.extend(cc.iter().map(|x| x.to_owned())); + result.push(cc.iter().map(|x| x.to_owned()).collect()); + } + + result + } +} diff --git a/src/graph/edge_types.rs b/src/graph/edge_types.rs new file mode 100644 index 0000000..60678ba --- /dev/null +++ b/src/graph/edge_types.rs @@ -0,0 +1,114 @@ +use std::{ + cmp::Ordering, + collections::{HashMap, HashSet}, + fmt::Debug, + hash::Hash, +}; + +use indicatif::ProgressBar; + +use super::AdjacencyGraph; + +#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub enum EdgeType { + TreeEdge, + BackEdge, + ForwardEdge, + CrossEdge, +} + +impl AdjacencyGraph +where + V: Hash + Eq + Clone + Debug, +{ + pub fn compute_edge_types(&self) -> HashMap<(&V, &V), EdgeType> { + /// To correctly compute the start and end times of the nodes in the + /// graph, we need to keep do work before and after the recursion call + enum RecurseState<'a, V> { + Before(&'a V), + BeforeNeighbor(&'a V, &'a V), + AfterNeighbor(&'a V), + } + + let mut edge_types = HashMap::new(); + + let mut visited = HashSet::new(); + let mut start_times = HashMap::new(); + let mut finished_nodes = HashSet::new(); + + let mut time = 0; + + let progress_bar = ProgressBar::new(self.nodes().len() as u64); + + for node in self.nodes().iter() { + if visited.contains(node) { + continue; + } + + let mut stack = Vec::new(); + + stack.push(RecurseState::Before(node)); + + while let Some(state) = stack.pop() { + match state { + RecurseState::Before(node) => { + progress_bar.inc(1); + visited.insert(node.clone()); + start_times.insert(node, time); + time += 1; + + // this is extremely important that is before the adjacencies to correctly + // iterate over the graph + + if let Some(adjacencies) = self.get_adjacencies(node) { + for adj in adjacencies { + println!("Node: {:?} Adj: {:?}", node, adj,); + + stack.push(RecurseState::AfterNeighbor(node)); + + if !visited.contains(adj) { + edge_types.insert((node, adj), EdgeType::TreeEdge); + stack.push(RecurseState::Before(adj)); + } else { + stack.push(RecurseState::BeforeNeighbor(node, adj)); + } + } + } + } + RecurseState::AfterNeighbor(node) => { + finished_nodes.insert(node, time); + time += 1; + } + RecurseState::BeforeNeighbor(node, adj) => { + let start_time_node = start_times.get(node).unwrap(); + let start_time_adj = start_times.get(adj).unwrap(); + let end_time_node = finished_nodes.get(node).unwrap_or(&0); + let end_time_adj = finished_nodes.get(adj).unwrap_or(&0); + + println!( + "Times: ({:?}, {:?}) ({:?}, {:?})", + start_time_node, end_time_node, start_time_adj, end_time_adj + ); + + match ( + start_time_node.cmp(start_time_adj), + end_time_node.cmp(end_time_adj), + ) { + (Ordering::Less, Ordering::Greater) => { + edge_types.insert((node, adj), EdgeType::ForwardEdge); + } + (Ordering::Greater, Ordering::Less) => { + edge_types.insert((node, adj), EdgeType::BackEdge); + } + _ => { + edge_types.insert((node, adj), EdgeType::CrossEdge); + } + } + } + } + } + } + + edge_types + } +} diff --git a/src/graph/mod.rs b/src/graph/mod.rs new file mode 100644 index 0000000..a8737c5 --- /dev/null +++ b/src/graph/mod.rs @@ -0,0 +1,57 @@ +use std::{ + collections::{HashMap, HashSet}, + fmt::Debug, + hash::Hash, +}; + +#[derive(Debug)] +pub struct AdjacencyGraph +where + V: Hash + Eq + Clone, +{ + nodes: HashSet, + adjacencies: HashMap>, +} + +pub struct UndirectedGraph +where + V: Hash + Eq + Clone, +{ + graph: AdjacencyGraph, +} + +pub mod algorithms; +pub mod edge_types; + +#[cfg(test)] +mod tests { + use std::collections::BTreeMap; + + use super::*; + + #[test] + fn test_compute_edge_types() { + let mut g = AdjacencyGraph::new(); + + g.add_edge(1, 2); + g.add_edge(2, 3); + g.add_edge(3, 4); + g.add_edge(4, 1); + + let edge_types = g.compute_edge_types(); + let edge_type_dict = + edge_types + .iter() + .fold(BTreeMap::new(), |mut acc, (edge, edge_type)| { + acc.entry(edge_type).or_insert_with(Vec::new).push(edge); + acc + }); + + for (edge_type, edges) in edge_type_dict.iter() { + println!("- {:?}", edge_type); + for edge in edges { + println!("Edge: {:?}", edge); + } + } + } +} diff --git a/src/graph.rs b/src/graphhhh.rs similarity index 68% rename from src/graph.rs rename to src/graphhhh.rs index 4b0144a..fcd3956 100644 --- a/src/graph.rs +++ b/src/graphhhh.rs @@ -1,5 +1,6 @@ use std::{ cell::RefCell, + cmp::Ordering, collections::{BTreeMap, HashMap, HashSet, VecDeque}, fmt::Debug, hash::Hash, @@ -25,6 +26,13 @@ where adjacencies: HashMap>, } +pub struct UndirectedGraph +where + V: Hash + Eq + Clone, +{ + graph: AdjacencyGraph, +} + #[allow(dead_code)] impl AdjacencyGraph where @@ -62,6 +70,10 @@ where &self.adjacencies } + pub fn nodes(&self) -> &HashSet { + &self.nodes + } + pub fn edges(&self) -> impl Iterator { self.adjacencies .iter() @@ -79,7 +91,7 @@ where opposite } - pub fn undirected(&self) -> AdjacencyGraph<&V> { + pub fn undirected(&self) -> UndirectedGraph<&V> { let mut undirected = AdjacencyGraph::new(); // O(|E|) @@ -88,7 +100,7 @@ where undirected.add_edge(to, from); } - undirected + UndirectedGraph { graph: undirected } } pub fn has_edge(&self, from: &V, to: &V) -> bool { @@ -165,123 +177,6 @@ where false } - pub fn compute_edge_types(&self) -> HashMap<(&V, &V), EdgeType> { - /// To correctly compute the start and end times of the nodes in the graph, we need to keep do work before and after the recursion - /// call - enum RecurseState { - Before, - AfterNeighbor, - } - - let mut edge_types = HashMap::new(); - - let mut visited = HashSet::new(); - let mut start_times = HashMap::new(); - let mut end_times = HashMap::new(); - - let mut time = 0; - - let progress_bar = ProgressBar::new(self.nodes.len() as u64); - - for node in self.nodes.iter() { - if visited.contains(node) { - continue; - } - - let mut stack = Vec::new(); - - stack.push((node, RecurseState::Before)); - - while let Some((node, state)) = stack.pop() { - match state { - RecurseState::Before => { - progress_bar.inc(1); - visited.insert(node.clone()); - start_times.insert(node, time); - time += 1; - - // this is extremely important that is before the adjacencies to correctly - // iterate over the graph - - if let Some(adjacencies) = self.get_adjacencies(node) { - for adj in adjacencies { - // if visited.contains(adj) { - // if start_times.get(adj) < start_times.get(node) { - // edge_types.insert((node, adj), EdgeType::BackEdge); - // } else { - // edge_types.insert((node, adj), EdgeType::CrossEdge); - // } - // } else { - // edge_types.insert((node, adj), EdgeType::ForwardEdge); - // stack.push((adj, RecurseState::Before)); - // } - - stack.push((node, RecurseState::AfterNeighbor)); - - if !visited.contains(adj) { - edge_types.insert((node, adj), EdgeType::TreeEdge); - stack.push((adj, RecurseState::Before)); - } else { - let start_time_node = start_times.get(node).unwrap(); - let start_time_adj = start_times.get(adj).unwrap(); - let end_time_node = end_times.get(node).unwrap_or(&0); - let end_time_adj = end_times.get(adj).unwrap_or(&0); - - if start_time_node < start_time_adj - && end_time_node > end_time_adj - { - edge_types.insert((node, adj), EdgeType::ForwardEdge); - } else if start_time_node > start_time_adj - && end_time_node < end_time_adj - { - edge_types.insert((node, adj), EdgeType::BackEdge); - // } else if start_time_node > start_time_adj - // && end_time_node > end_time_adj - // { - // edge_types.insert((node, adj), EdgeType::CrossEdge); - } else { - edge_types.insert((node, adj), EdgeType::CrossEdge); - } - } - } - } - } - RecurseState::AfterNeighbor => { - end_times.insert(node, time); - time += 1; - } - } - } - } - - // for node in self.nodes.iter() { - // let mut stack = Vec::new(); - - // if visited.contains(node) { - // continue; - // } - - // stack.push(node); - - // while let Some(node) = stack.pop() { - // visited.insert(node.clone()); - - // if let Some(adjacencies) = self.get_adjacencies(node) { - // for adj in adjacencies { - // if visited.contains(adj) { - // // ... - // } else { - // edge_types.insert((node, adj), EdgeType::TreeEdge); - // stack.push(adj); - // } - // } - // } - // } - // } - - edge_types - } - pub fn shortest_path_matrix(&self) -> HashMap<&V, HashMap<&V, usize>> { let mut result = HashMap::new(); @@ -481,3 +376,41 @@ where } } } + +impl UndirectedGraph +where + V: Hash + Eq + Clone + Debug, +{ + pub fn connected_components(&self) -> Vec> { + let mut visited = HashSet::new(); + let mut result = Vec::new(); + + for node in self.graph.nodes.iter() { + if visited.contains(node) { + continue; + } + + let mut cc: HashSet = HashSet::new(); + let mut stack: Vec<&V> = vec![node]; + + while let Some(node) = stack.pop() { + if cc.contains(node) { + continue; + } + + cc.insert(node.clone()); + + if let Some(adjacencies) = self.graph.get_adjacencies(&node) { + for adj in adjacencies { + stack.push(adj); + } + } + } + + visited.extend(cc.iter().map(|x| x.to_owned())); + result.push(cc.iter().map(|x| x.to_owned()).collect()); + } + + result + } +} diff --git a/src/lib.rs b/src/lib.rs index 6865f44..79cc1ad 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,3 @@ -pub mod adv_graph; pub mod gfa; pub mod graph; pub mod graph_2; diff --git a/src/main.rs b/src/main.rs index 8fa440a..240e4ed 100644 --- a/src/main.rs +++ b/src/main.rs @@ -93,7 +93,7 @@ fn main() -> std::io::Result<()> { // graph.print_stats(); - println!("Graph has cycles: {}", graph.is_cyclic()); + // println!("Graph has cycles: {}", graph.is_cyclic()); let edge_types = graph.compute_edge_types(); @@ -107,6 +107,26 @@ fn main() -> std::io::Result<()> { println!("Edge types histogram: {:?}", edge_type_histogram); + // println!("Convert to undirected graph..."); + // let undir_graph = graph.undirected(); + + // println!("Computing connected components..."); + // let cc = undir_graph.connected_components(); + + // println!("Computing histogram..."); + // let cc_histogram: BTreeMap<_, _> = cc + // .iter() + // .map(|cc| cc.len()) // map to size of each cc + // .fold(BTreeMap::new(), |mut acc, len| { + // *acc.entry(len).or_insert(0) += 1; + // acc + // }); + + // println!("Connected Components Size Histogram:"); + // for (size, count) in cc_histogram.iter() { + // println!("{}: {}", size, count); + // } + println!("Cleaning up..."); } }