mirror of https://github.com/aziis98/asd-2024.git
mid thorugh refactoring
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue