generic graph code and visualization

This commit is contained in:
2024-05-24 01:38:04 +02:00
parent e654535280
commit ce6dcb0551
12 changed files with 330 additions and 222 deletions
+12 -1
View File
@@ -1,9 +1,20 @@
#[derive(Debug)]
use std::fmt::Display;
#[derive(Debug, Hash, PartialEq, Eq, Clone)]
pub enum Orientation {
Forward,
Reverse,
}
impl Display for Orientation {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Orientation::Forward => write!(f, "+"),
Orientation::Reverse => write!(f, "-"),
}
}
}
#[derive(Debug)]
pub enum Entry {
Header {
+72 -50
View File
@@ -1,100 +1,120 @@
use std::{
cell::RefCell,
collections::{HashMap, HashSet},
fmt::Debug,
hash::Hash,
rc::Rc,
};
#[derive(Debug)]
pub struct AdjacencyGraph {
nodes: HashMap<String, String>,
adjacencies: HashMap<String, HashSet<String>>,
pub struct AdjacencyGraph<V>
where
V: Hash + Eq + Clone,
{
nodes: HashSet<V>,
adjacencies: HashMap<V, HashSet<V>>,
}
#[allow(dead_code)]
impl AdjacencyGraph {
impl<V> AdjacencyGraph<V>
where
V: Hash + Eq + Clone + Debug,
{
pub fn new() -> Self {
AdjacencyGraph {
nodes: HashMap::new(),
nodes: HashSet::new(),
adjacencies: HashMap::new(),
}
}
pub fn add_node(&mut self, key: String, value: String) {
self.nodes.insert(key, value);
pub fn add_node(&mut self, node: V) {
// O(1)
self.nodes.insert(node);
}
pub fn add_edge(&mut self, from: String, to: String) {
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(HashSet::new())
.or_insert_with(HashSet::new)
.insert(to);
}
pub fn get_adjacencies(&self, node: &str) -> Option<&HashSet<String>> {
pub fn get_adjacencies(&self, node: &V) -> Option<&HashSet<V>> {
self.adjacencies.get(node)
}
pub fn adjacencies(&self) -> &HashMap<String, HashSet<String>> {
pub fn adjacencies(&self) -> &HashMap<V, HashSet<V>> {
&self.adjacencies
}
pub fn opposite(&self) -> AdjacencyGraph {
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();
for (from, adjacencies) in self.adjacencies.iter() {
for to in adjacencies {
opposite.add_edge(to.clone(), from.clone());
}
// O(|E|)
for (from, to) in self.edges() {
opposite.add_edge(to, from);
}
opposite
}
pub fn has_edge(&self, from: &str, to: &str) -> bool {
pub fn has_edge(&self, from: &V, to: &V) -> bool {
// O(1)
if let Some(adjacencies) = self.get_adjacencies(from) {
adjacencies.contains(&to.to_string())
// O(1)
adjacencies.contains(&to.to_owned())
} else {
false
}
}
// pub fn dfs(&self, node: String) -> HashSet<String> {
// let mut visited = HashSet::new();
// let mut stack = vec![node];
pub fn dfs(&self, node: V) -> HashSet<V> {
let mut visited: HashSet<V> = HashSet::new();
let mut stack = vec![&node];
// while let Some(node) = stack.pop() {
// if visited.contains(&node) {
// continue;
// }
// O(|V| + |E|)
while let Some(node) = stack.pop() {
visited.insert(node.clone());
// visited.insert(node.clone());
if let Some(adjacencies) = self.get_adjacencies(&node) {
for adj in adjacencies {
if !visited.contains(adj) {
stack.push(adj);
}
}
}
}
// if let Some(adjacencies) = self.get_adjacencies(&node) {
// for adj in adjacencies {
// stack.push(adj.clone());
// }
// }
// }
visited
}
// visited
// }
pub fn compute_ccs(&self) -> Vec<Vec<String>> {
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.keys() {
for node in self.nodes.iter() {
if visited.contains(node) {
continue;
}
let mut cc = HashSet::new();
let mut stack = vec![node.to_string()];
let mut cc: HashSet<V> = HashSet::new();
let mut stack: Vec<&V> = vec![node];
while let Some(node) = stack.pop() {
if cc.contains(&node) {
if cc.contains(node) {
continue;
}
@@ -102,17 +122,19 @@ impl AdjacencyGraph {
if let Some(adjacencies) = self.get_adjacencies(&node) {
for adj in adjacencies {
stack.push(adj.clone());
stack.push(adj);
}
}
if let Some(adjacencies) = op.get_adjacencies(&node) {
for adj in adjacencies {
stack.push(adj.clone());
stack.push(adj);
}
}
}
// println!("CC: {:?}", cc);
visited.extend(cc.iter().map(|x| x.to_owned()));
result.push(cc.iter().map(|x| x.to_owned()).collect());
}
@@ -120,11 +142,11 @@ impl AdjacencyGraph {
result
}
pub fn compute_ccs_2(&self) -> Vec<Vec<String>> {
let mut cc = HashMap::<String, Rc<RefCell<HashSet<String>>>>::new();
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.keys() {
if cc.contains_key(node) {
for node in self.nodes.iter() {
if cc.contains_key(&node) {
continue;
}
@@ -132,7 +154,7 @@ impl AdjacencyGraph {
let new_cc = Rc::new(RefCell::new(HashSet::new()));
let mut stack = vec![node.to_string()];
let mut stack: Vec<&V> = vec![node];
while let Some(node) = stack.pop() {
println!("New CC: {:?}", new_cc.borrow());
@@ -140,7 +162,7 @@ impl AdjacencyGraph {
if cc.contains_key(&node) {
// merge the two connected components and go to the next node
let old_cc = cc.get(&node).unwrap();
let old_cc: &Rc<RefCell<HashSet<V>>> = cc.get(&node).unwrap();
println!(
"Merging {:?} with {:?} due to link to {:?}",
@@ -164,7 +186,7 @@ impl AdjacencyGraph {
if let Some(adjacencies) = self.get_adjacencies(&node) {
for adj in adjacencies {
stack.push(adj.clone());
stack.push(adj);
}
}
}
@@ -178,7 +200,7 @@ impl AdjacencyGraph {
let mut result = Vec::new();
let mut seen = HashSet::new();
for node in self.nodes.keys() {
for node in self.nodes.iter() {
if seen.contains(node) {
continue;
}
+3
View File
@@ -0,0 +1,3 @@
pub mod gfa;
pub mod graph;
pub mod parser;
+24 -25
View File
@@ -1,3 +1,5 @@
use std::collections::HashMap;
use argh::FromArgs;
use gfa::{Entry, Orientation};
use graph::AdjacencyGraph;
@@ -36,48 +38,45 @@ fn main() -> std::io::Result<()> {
let file = std::fs::File::open(show.input)?;
let entries = parser::parse_source(file)?;
let mut graph = AdjacencyGraph::new();
let mut sequence_map = HashMap::new();
let mut graph: AdjacencyGraph<(String, Orientation)> = AdjacencyGraph::new();
for entry in entries {
println!("{:?}", entry);
match entry {
Entry::Segment { id, sequence } => {
graph.add_node(id, sequence);
sequence_map.insert(id.clone(), sequence);
}
Entry::Link {
from,
from_orient,
to,
to_orient,
} => match (from_orient, to_orient) {
(Orientation::Forward, Orientation::Forward)
| (Orientation::Reverse, Orientation::Reverse) => {
graph.add_edge(from, to);
}
(Orientation::Forward, Orientation::Reverse)
| (Orientation::Reverse, Orientation::Forward) => {
graph.add_edge(to, from);
}
},
} => {
graph.add_edge((from.clone(), from_orient), (to.clone(), to_orient));
}
_ => {}
}
}
for (from, adjacencies) in graph.adjacencies().iter() {
println!(
"{} -> {}",
from,
adjacencies
.iter()
.map(|to| to.to_owned())
.collect::<Vec<String>>()
.join(", ")
);
}
// Print the graph
// for ((from, orient), adjacencies) in graph.adjacencies().iter() {
// println!(
// "{}{} -> {}",
// from,
// orient,
// adjacencies
// .iter()
// .map(|(to, orient)| format!("{}{}", to, orient))
// .collect::<Vec<String>>()
// .join(", ")
// );
// }
let cc = graph.compute_ccs_2();
println!("CCs: {:?}", cc);
let cc = graph.compute_ccs();
// println!("CCs: {:?}", cc);
println!("Number of connected components: {}", cc.len());
}
}
+2 -2
View File
@@ -84,7 +84,7 @@ fn parse_path_segments(s: &str) -> Vec<(String, Orientation)> {
let mut rest = s;
loop {
println!("Rest: {}", rest);
// println!("Rest: {}", rest);
let r = rest;
@@ -132,7 +132,7 @@ pub fn parse_source<R: Read>(reader: R) -> io::Result<Vec<Entry>> {
continue;
}
println!("Parsing: {}", line);
// println!("Parsing: {}", line);
let first_char = line.chars().next().unwrap();
let entry = match first_char {