mid thorugh refactoring

main
Antonio De Lucreziis 1 month ago
parent f19862206e
commit 37bc313f20

@ -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()

@ -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)

@ -1,16 +0,0 @@
use std::{collections::HashMap, hash::Hash};
struct GraphEdge {
from: u32,
to: u32,
}
pub struct Graph<V, E>
where
V: Hash + Eq + Clone,
{
nodes: HashMap<V, u32>,
edges: HashMap<(u32, u32), E>,
adjacency_list: HashMap<u32, Vec<(u32, u32)>>,
}

@ -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<V> AdjacencyGraph<V>
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<V>> {
self.adjacencies.get(node)
}
pub fn adjacencies(&self) -> &HashMap<V, HashSet<V>> {
&self.adjacencies
}
pub fn nodes(&self) -> &HashSet<V> {
&self.nodes
}
pub fn edges(&self) -> impl Iterator<Item = (&V, &V)> {
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<Item = V> + '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::<HashSet<_>>();
// 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<Vec<V>> {
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<V> = 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<Vec<V>> {
let mut cc: HashMap<V, Rc<RefCell<HashSet<V>>>> = 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<RefCell<HashSet<V>>> = 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<usize, usize> = 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<V> UndirectedGraph<V>
where
V: Hash + Eq + Clone + Debug,
{
pub fn connected_components(&self) -> Vec<Vec<V>> {
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<V> = 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
}
}

@ -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<V> AdjacencyGraph<V>
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
}
}

@ -0,0 +1,57 @@
use std::{
collections::{HashMap, HashSet},
fmt::Debug,
hash::Hash,
};
#[derive(Debug)]
pub struct AdjacencyGraph<V>
where
V: Hash + Eq + Clone,
{
nodes: HashSet<V>,
adjacencies: HashMap<V, HashSet<V>>,
}
pub struct UndirectedGraph<V>
where
V: Hash + Eq + Clone,
{
graph: AdjacencyGraph<V>,
}
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);
}
}
}
}

@ -1,5 +1,6 @@
use std::{ use std::{
cell::RefCell, cell::RefCell,
cmp::Ordering,
collections::{BTreeMap, HashMap, HashSet, VecDeque}, collections::{BTreeMap, HashMap, HashSet, VecDeque},
fmt::Debug, fmt::Debug,
hash::Hash, hash::Hash,
@ -25,6 +26,13 @@ where
adjacencies: HashMap<V, HashSet<V>>, adjacencies: HashMap<V, HashSet<V>>,
} }
pub struct UndirectedGraph<V>
where
V: Hash + Eq + Clone,
{
graph: AdjacencyGraph<V>,
}
#[allow(dead_code)] #[allow(dead_code)]
impl<V> AdjacencyGraph<V> impl<V> AdjacencyGraph<V>
where where
@ -62,6 +70,10 @@ where
&self.adjacencies &self.adjacencies
} }
pub fn nodes(&self) -> &HashSet<V> {
&self.nodes
}
pub fn edges(&self) -> impl Iterator<Item = (&V, &V)> { pub fn edges(&self) -> impl Iterator<Item = (&V, &V)> {
self.adjacencies self.adjacencies
.iter() .iter()
@ -79,7 +91,7 @@ where
opposite opposite
} }
pub fn undirected(&self) -> AdjacencyGraph<&V> { pub fn undirected(&self) -> UndirectedGraph<&V> {
let mut undirected = AdjacencyGraph::new(); let mut undirected = AdjacencyGraph::new();
// O(|E|) // O(|E|)
@ -88,7 +100,7 @@ where
undirected.add_edge(to, from); undirected.add_edge(to, from);
} }
undirected UndirectedGraph { graph: undirected }
} }
pub fn has_edge(&self, from: &V, to: &V) -> bool { pub fn has_edge(&self, from: &V, to: &V) -> bool {
@ -165,123 +177,6 @@ where
false 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>> { pub fn shortest_path_matrix(&self) -> HashMap<&V, HashMap<&V, usize>> {
let mut result = HashMap::new(); let mut result = HashMap::new();
@ -481,3 +376,41 @@ where
} }
} }
} }
impl<V> UndirectedGraph<V>
where
V: Hash + Eq + Clone + Debug,
{
pub fn connected_components(&self) -> Vec<Vec<V>> {
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<V> = 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
}
}

@ -1,4 +1,3 @@
pub mod adv_graph;
pub mod gfa; pub mod gfa;
pub mod graph; pub mod graph;
pub mod graph_2; pub mod graph_2;

@ -93,7 +93,7 @@ fn main() -> std::io::Result<()> {
// graph.print_stats(); // graph.print_stats();
println!("Graph has cycles: {}", graph.is_cyclic()); // println!("Graph has cycles: {}", graph.is_cyclic());
let edge_types = graph.compute_edge_types(); let edge_types = graph.compute_edge_types();
@ -107,6 +107,26 @@ fn main() -> std::io::Result<()> {
println!("Edge types histogram: {:?}", edge_type_histogram); 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..."); println!("Cleaning up...");
} }
} }

Loading…
Cancel
Save