Skip to content
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ authors = ["Aram Ebtekar <aram.eb@mythic-ai.com>"]
edition = "2018"

[dependencies]
bit-vec = "*"
3 changes: 2 additions & 1 deletion src/graph/connectivity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,8 @@ impl<'a> ConnectivityGraph<'a> {
} else {
Some(scc_true < scc_false)
}
}).collect()
})
.collect()
}

/// Gets the vertices of a directed acyclic graph (DAG) in topological
Expand Down
121 changes: 121 additions & 0 deletions src/graph/dfs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
use super::Graph;
use crate::graph::AdjListIterator;
use bit_vec::BitVec;

impl Graph {
pub fn dfs(&self, v: usize) -> DfsIterator {
// Create a stack for DFS
let mut stack: Vec<usize> = Vec::new();

let adj_iters = (0..self.num_v())
.map(|u| self.adj_list(u))
.collect::<Vec<_>>();

// Push the current source node.
stack.push(v);

DfsIterator {
visited: BitVec::from_elem(self.num_v(), false),
stack,
adj_iters,
}
}
}
pub struct DfsIterator<'a> {
//is vertex visited
visited: BitVec,
//stack of vertices
stack: Vec<usize>,
adj_iters: Vec<AdjListIterator<'a>>,
}

impl<'a> Iterator for DfsIterator<'a> {
type Item = usize;

/// Returns next vertex in the DFS
fn next(&mut self) -> Option<Self::Item> {
//Sources:
// https://www.geeksforgeeks.org/iterative-depth-first-traversal/
// https://en.wikipedia.org/wiki/Depth-first_search
while let Some(&s) = self.stack.last() {

//Does s still have neighbors we need to process?
if let Some((_,s_nbr)) = self.adj_iters[s].next() {
if !self.visited[s_nbr] {
self.stack.push(s_nbr);
}
} else {
//s has no more neighbors, we can pop it off the stack
self.stack.pop();
}

// Stack may contain same vertex twice. So
// we return the popped item only
// if it is not visited.
if !self.visited[s] {
self.visited.set(s, true);
return Some(s);
}
}

None
}
}

#[cfg(test)]
mod test {
use super::*;

#[test]
fn test_dfs() {
let mut graph = Graph::new(4, 8);
graph.add_edge(0, 2);
graph.add_edge(2, 0);
graph.add_edge(1, 2);
graph.add_edge(0, 1);
graph.add_edge(3, 3);
graph.add_edge(2, 3);

let dfs_search = graph.dfs(2).collect::<Vec<_>>();
assert_eq!(dfs_search, vec![2, 3, 0, 1]);
}

#[test]
fn test_dfs2() {
let mut graph = Graph::new(5, 8);
graph.add_edge(0, 2);
graph.add_edge(2, 1);
graph.add_edge(1, 0);
graph.add_edge(0, 3);
graph.add_edge(3, 4);
graph.add_edge(4, 0);

let dfs_search = graph.dfs(0).collect::<Vec<_>>();
//Note this is not the only valid DFS
assert_eq!(dfs_search, vec![0, 3, 4, 2, 1]);
}

#[test]
fn test_dfs_space_complexity() {
let num_v = 20;
let mut graph = Graph::new(num_v, 0);
for i in 0..num_v {
for j in 0..num_v {
graph.add_undirected_edge(i, j);
}
}

let mut dfs_search = graph.dfs(7);
let mut dfs_check = vec![];
for _ in 0..num_v {
dfs_check.push(dfs_search.next().unwrap());
assert!(dfs_search.stack.len() <= num_v+1);
}

dfs_check.sort();
dfs_check.dedup();
assert_eq!(0, dfs_check[0]);
assert_eq!(num_v, dfs_check.len());
assert_eq!(num_v-1, dfs_check[num_v-1]);
}
}
65 changes: 58 additions & 7 deletions src/graph/flow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ impl FlowGraph {
/// # Panics
///
/// Panics if the maximum flow is 2^63 or larger.
pub fn dinic(&self, s: usize, t: usize) -> i64 {
pub fn dinic(&self, s: usize, t: usize) -> (i64, Vec<i64>) {
let mut flow = vec![0; self.graph.num_e()];
let mut max_flow = 0;
loop {
Expand All @@ -56,7 +56,7 @@ impl FlowGraph {
.collect::<Vec<_>>();
max_flow += self.dinic_augment(s, t, Self::INF, &dist, &mut adj_iters, &mut flow);
}
max_flow
(max_flow, flow)
}

// Compute BFS distances to restrict attention to shortest path edges.
Expand Down Expand Up @@ -115,7 +115,8 @@ impl FlowGraph {
let u = self.graph.endp[e ^ 1];
let v = self.graph.endp[e];
dist[u] < Self::INF && dist[v] == Self::INF
}).collect()
})
.collect()
}

/// Among all s-t maximum flows, finds one with minimum cost, assuming
Expand All @@ -124,7 +125,7 @@ impl FlowGraph {
/// # Panics
///
/// Panics if the flow or cost overflow a 64-bit signed integer.
pub fn mcf(&self, s: usize, t: usize) -> (i64, i64) {
pub fn mcf(&self, s: usize, t: usize) -> (i64, i64, Vec<i64>) {
let mut pot = vec![0; self.graph.num_v()];

// Bellman-Ford deals with negative-cost edges at initialization.
Expand All @@ -149,7 +150,7 @@ impl FlowGraph {
min_cost += dc;
max_flow += df;
}
(min_cost, max_flow)
(min_cost, max_flow, flow)
}

// Maintains Johnson's potentials to prevent negative-cost residual edges.
Expand Down Expand Up @@ -205,7 +206,7 @@ mod test {
graph.add_edge(0, 1, 4, 1);
graph.add_edge(1, 2, 3, 1);

let flow = graph.dinic(0, 2);
let flow = graph.dinic(0, 2).0;
assert_eq!(flow, 3);
}

Expand All @@ -217,8 +218,58 @@ mod test {
graph.add_edge(2, 3, 7, 8);
graph.add_edge(1, 3, 7, 10);

let (cost, flow) = graph.mcf(0, 3);
let (cost, flow, _) = graph.mcf(0, 3);
assert_eq!(cost, 18);
assert_eq!(flow, 10);
}

#[test]
fn test_max_matching() {
let mut graph = FlowGraph::new(14, 4);

let source = 0;
let sink = 13;

//Vertex indices of "left hand side" of bipartite graph go from [left_start, right_start)
let left_start = 1;
//Vertex indices of "right hand side" of bipartite graph go from [right_start, sink)
let right_start = 7;

//Initialize source / sink connections; both left & right have 6 nodes
for lhs_vertex in left_start..left_start + 6 {
graph.add_edge(source, lhs_vertex, 1, 1);
}

for rhs_vertex in right_start..right_start + 6 {
graph.add_edge(rhs_vertex, sink, 1, 1);
}

graph.add_edge(left_start + 0, right_start + 1, 1, 1);
graph.add_edge(left_start + 0, right_start + 2, 1, 1);
graph.add_edge(left_start + 2, right_start + 0, 1, 1);
graph.add_edge(left_start + 2, right_start + 3, 1, 1);
graph.add_edge(left_start + 3, right_start + 2, 1, 1);
graph.add_edge(left_start + 4, right_start + 2, 1, 1);
graph.add_edge(left_start + 4, right_start + 3, 1, 1);
graph.add_edge(left_start + 5, right_start + 5, 1, 1);

let (flow_amt, flow) = graph.dinic(source, sink);
assert_eq!(flow_amt, 5);

//L->R edges in maximum matching
let left_right_edges = flow
.into_iter()
.enumerate()
.filter(|&(_e, f)| f > 0)
//map to u->v
.map(|(e, _f)| (graph.graph.endp[e ^ 1], graph.graph.endp[e]))
//leave out source and sink nodes
.filter(|&(u, v)| u != source && v != sink)
.collect::<Vec<_>>();

assert_eq!(
left_right_edges,
vec![(1, 8), (3, 7), (4, 9), (5, 10), (6, 12)]
);
}
}
1 change: 1 addition & 0 deletions src/graph/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
//!
//! All methods will panic if given an out-of-bounds element index.
pub mod connectivity;
mod dfs;
pub mod flow;

/// Represents a union of disjoint sets. Each set's elements are arranged in a
Expand Down