use std::collections::{BTreeSet, VecDeque};
use super::graph::LayoutGraph;
use super::rank_core;
pub(crate) fn slack(graph: &LayoutGraph, edge_idx: usize) -> i32 {
let edges = graph.effective_edges();
let (from, to) = edges[edge_idx];
graph.ranks[to] - graph.ranks[from] - graph.edge_minlens[edge_idx]
}
pub(crate) struct SpanningTree {
pub parent: Vec<Option<usize>>,
pub parent_edge: Vec<Option<usize>>,
pub in_tree: Vec<bool>,
size: usize,
pub low: Vec<i32>,
pub lim: Vec<i32>,
pub cut_value: Vec<f64>,
pub tree_edges: BTreeSet<usize>,
}
impl SpanningTree {
fn new(n: usize) -> Self {
SpanningTree {
parent: vec![None; n],
parent_edge: vec![None; n],
in_tree: vec![false; n],
size: 0,
low: vec![0; n],
lim: vec![0; n],
cut_value: vec![0.0; n],
tree_edges: BTreeSet::new(),
}
}
fn add_node(&mut self, node: usize) {
if !self.in_tree[node] {
self.in_tree[node] = true;
self.size += 1;
}
}
fn add_edge(&mut self, parent: usize, child: usize, edge_idx: usize) {
self.add_node(child);
self.parent[child] = Some(parent);
self.parent_edge[child] = Some(edge_idx);
self.tree_edges.insert(edge_idx);
}
fn root(&self) -> usize {
(0..self.parent.len())
.find(|&n| self.in_tree[n] && self.parent[n].is_none())
.unwrap_or(0)
}
pub fn node_count(&self) -> usize {
self.in_tree.len()
}
pub fn size(&self) -> usize {
self.size
}
}
fn build_adjacency(graph: &LayoutGraph) -> Vec<Vec<(usize, usize)>> {
let n = graph.node_count();
let edges = graph.effective_edges();
let mut adj = vec![Vec::new(); n];
for (edge_idx, &(from, to)) in edges.iter().enumerate() {
adj[from].push((to, edge_idx));
adj[to].push((from, edge_idx));
}
adj
}
fn tight_tree(tree: &mut SpanningTree, graph: &LayoutGraph, adj: &[Vec<(usize, usize)>]) -> usize {
let tree_nodes: Vec<usize> = (0..tree.node_count())
.filter(|&n| tree.in_tree[n])
.collect();
let mut stack: Vec<usize> = tree_nodes;
while let Some(v) = stack.pop() {
for &(w, edge_idx) in &adj[v] {
if !tree.in_tree[w] && slack(graph, edge_idx) == 0 {
tree.add_edge(v, w, edge_idx);
stack.push(w);
}
}
}
tree.size()
}
fn find_min_slack_crossing(tree: &SpanningTree, graph: &LayoutGraph) -> Option<(usize, i32)> {
let edges = graph.effective_edges();
let mut best_edge = None;
let mut best_slack = i32::MAX;
for (edge_idx, &(from, to)) in edges.iter().enumerate() {
let from_in = tree.in_tree[from];
let to_in = tree.in_tree[to];
if from_in == to_in {
continue; }
let s = slack(graph, edge_idx);
if s < best_slack {
best_slack = s;
best_edge = Some(edge_idx);
}
}
let best_edge = best_edge?;
let (from, _to) = edges[best_edge];
let raw_slack = slack(graph, best_edge);
let delta = if tree.in_tree[from] {
raw_slack
} else {
-raw_slack
};
Some((best_edge, delta))
}
fn shift_ranks(tree: &SpanningTree, graph: &mut LayoutGraph, delta: i32) {
for (node, &in_tree) in tree.in_tree.iter().enumerate() {
if in_tree {
graph.ranks[node] += delta;
}
}
}
pub(crate) fn init_low_lim(tree: &mut SpanningTree, root: usize) {
let n = tree.parent.len();
let mut children: Vec<Vec<usize>> = vec![Vec::new(); n];
for node in 0..n {
if let Some(p) = tree.parent[node] {
children[p].push(node);
}
}
let mut counter = 1i32;
let mut stack: Vec<(usize, bool)> = vec![(root, false)];
while let Some((node, post)) = stack.pop() {
if post {
tree.lim[node] = counter;
counter += 1;
} else {
tree.low[node] = counter;
stack.push((node, true));
for &child in children[node].iter().rev() {
stack.push((child, false));
}
}
}
}
pub(crate) fn is_descendant(tree: &SpanningTree, u: usize, v: usize) -> bool {
tree.low[v] <= tree.lim[u] && tree.lim[u] <= tree.lim[v]
}
pub(crate) fn init_cut_values(tree: &mut SpanningTree, graph: &LayoutGraph) {
let n = tree.parent.len();
let edges = graph.effective_edges();
let mut children: Vec<Vec<usize>> = vec![Vec::new(); n];
for node in 0..n {
if let Some(p) = tree.parent[node] {
children[p].push(node);
}
}
let postorder = postorder_from_children(&children, tree);
for &node in &postorder {
if tree.parent[node].is_none() {
continue; }
tree.cut_value[node] = calc_cut_value(tree, graph, node, &edges);
}
}
fn postorder_from_children(children: &[Vec<usize>], tree: &SpanningTree) -> Vec<usize> {
let mut result = Vec::new();
let root = (0..tree.parent.len())
.find(|&n| tree.in_tree[n] && tree.parent[n].is_none())
.unwrap_or(0);
let mut stack: Vec<(usize, bool)> = vec![(root, false)];
while let Some((node, post)) = stack.pop() {
if post {
result.push(node);
} else {
stack.push((node, true));
for &child in children[node].iter().rev() {
stack.push((child, false));
}
}
}
result
}
fn calc_cut_value(
tree: &SpanningTree,
graph: &LayoutGraph,
child: usize,
edges: &[(usize, usize)],
) -> f64 {
let parent = tree.parent[child].unwrap();
let tree_edge_idx = tree.parent_edge[child].unwrap();
let (from, _to) = edges[tree_edge_idx];
let child_is_tail = from == child;
let mut cut = graph.edge_weights[tree_edge_idx];
for (edge_idx, &(e_from, e_to)) in edges.iter().enumerate() {
if edge_idx == tree_edge_idx {
continue;
}
let is_out_edge;
let other;
if e_from == child {
is_out_edge = true;
other = e_to;
} else if e_to == child {
is_out_edge = false;
other = e_from;
} else {
continue; }
if other == parent {
continue;
}
let points_to_head = is_out_edge == child_is_tail;
let w = graph.edge_weights[edge_idx];
cut += if points_to_head { w } else { -w };
if tree.parent[other] == Some(child) && tree.parent_edge[other].is_some() {
let other_cut = tree.cut_value[other];
cut += if points_to_head {
-other_cut
} else {
other_cut
};
}
}
cut
}
pub(crate) fn feasible_tree(graph: &mut LayoutGraph) -> SpanningTree {
let n = graph.node_count();
let mut tree = SpanningTree::new(n);
let adj = build_adjacency(graph);
tree.add_node(0);
let max_iters = n * 2; let mut iters = 0;
loop {
let size = tight_tree(&mut tree, graph, &adj);
if size >= n {
break;
}
match find_min_slack_crossing(&tree, graph) {
Some((_edge_idx, delta)) => {
if delta == 0 {
for node in 0..n {
tree.add_node(node);
}
break;
}
shift_ranks(&tree, graph, delta);
}
None => {
for node in 0..n {
tree.add_node(node);
}
break;
}
}
iters += 1;
if iters >= max_iters {
for node in 0..n {
tree.add_node(node);
}
break;
}
}
tree
}
pub(crate) fn run(graph: &mut LayoutGraph) {
let n = graph.node_count();
let edge_count = graph.effective_edges().len();
if n <= 1 || edge_count == 0 {
rank_core::longest_path(graph);
return;
}
rank_core::longest_path(graph);
let mut tree = feasible_tree(graph);
let root = tree.root();
init_low_lim(&mut tree, root);
init_cut_values(&mut tree, graph);
let max_iters = graph.node_count() * graph.effective_edges().len().max(1);
let mut iters = 0;
while let Some(leave_node) = leave_edge(&tree) {
let enter_idx = match enter_edge(&tree, graph, leave_node) {
Some(idx) => idx,
None => break, };
exchange_edges(&mut tree, graph, leave_node, enter_idx);
iters += 1;
if iters >= max_iters {
break; }
}
rank_core::normalize(graph);
}
fn leave_edge(tree: &SpanningTree) -> Option<usize> {
(0..tree.parent.len()).find(|&node| tree.parent[node].is_some() && tree.cut_value[node] < 0.0)
}
fn enter_edge(tree: &SpanningTree, graph: &LayoutGraph, leave_node: usize) -> Option<usize> {
let edges = graph.effective_edges();
let leave_edge_idx = tree.parent_edge[leave_node].unwrap();
let (tail, head) = edges[leave_edge_idx];
let mut tail_root = tail;
let flip = tree.lim[tail] > tree.lim[head];
if flip {
tail_root = head;
}
let mut best_edge = None;
let mut best_slack = i32::MAX;
for (edge_idx, &(e_from, e_to)) in edges.iter().enumerate() {
if tree.tree_edges.contains(&edge_idx) {
continue; }
let from_desc = is_descendant(tree, e_from, tail_root);
let to_desc = is_descendant(tree, e_to, tail_root);
if from_desc != flip || to_desc == flip {
continue; }
let s = slack(graph, edge_idx);
if s < best_slack {
best_slack = s;
best_edge = Some(edge_idx);
}
}
best_edge
}
fn exchange_edges(
tree: &mut SpanningTree,
graph: &mut LayoutGraph,
leave_node: usize,
enter_edge_idx: usize,
) {
let leave_edge_idx = tree.parent_edge[leave_node].unwrap();
tree.tree_edges.remove(&leave_edge_idx);
tree.parent[leave_node] = None;
tree.parent_edge[leave_node] = None;
tree.tree_edges.insert(enter_edge_idx);
rebuild_parent_pointers(tree, graph);
let root = tree.root();
init_low_lim(tree, root);
init_cut_values(tree, graph);
update_ranks(tree, graph);
}
fn rebuild_parent_pointers(tree: &mut SpanningTree, graph: &LayoutGraph) {
let n = tree.parent.len();
let edges = graph.effective_edges();
for i in 0..n {
tree.parent[i] = None;
tree.parent_edge[i] = None;
}
let mut adj: Vec<Vec<(usize, usize)>> = vec![Vec::new(); n];
for &edge_idx in &tree.tree_edges {
let (from, to) = edges[edge_idx];
adj[from].push((to, edge_idx));
adj[to].push((from, edge_idx));
}
let root = (0..n).find(|&i| tree.in_tree[i]).unwrap_or(0);
let mut visited = vec![false; n];
let mut stack = vec![root];
visited[root] = true;
while let Some(node) = stack.pop() {
for &(neighbor, edge_idx) in &adj[node] {
if !visited[neighbor] {
visited[neighbor] = true;
tree.parent[neighbor] = Some(node);
tree.parent_edge[neighbor] = Some(edge_idx);
stack.push(neighbor);
}
}
}
}
fn update_ranks(tree: &SpanningTree, graph: &mut LayoutGraph) {
let n = graph.node_count();
let edges = graph.effective_edges();
let root = tree.root();
let mut children: Vec<Vec<usize>> = vec![Vec::new(); n];
for node in 0..n {
if let Some(p) = tree.parent[node] {
children[p].push(node);
}
}
let mut queue = VecDeque::new();
queue.push_back(root);
let mut visited = vec![false; n];
visited[root] = true;
while let Some(node) = queue.pop_front() {
for &child in &children[node] {
if visited[child] {
continue;
}
let edge_idx = tree.parent_edge[child].unwrap();
let (from, _to) = edges[edge_idx];
let minlen = graph.edge_minlens[edge_idx];
if from == child {
graph.ranks[child] = graph.ranks[node] - minlen;
} else {
graph.ranks[child] = graph.ranks[node] + minlen;
}
visited[child] = true;
queue.push_back(child);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::engines::graph::algorithms::layered::graph::{DiGraph, LayoutGraph};
use crate::engines::graph::algorithms::layered::rank;
fn make_chain_graph() -> LayoutGraph {
let mut g: DiGraph<()> = DiGraph::new();
g.add_node("A", ());
g.add_node("B", ());
g.add_node("C", ());
g.add_edge("A", "B");
g.add_edge("B", "C");
let mut lg = LayoutGraph::from_digraph(&g, |_, _| (10.0, 10.0));
lg.ranks = vec![0, 1, 3];
lg
}
fn make_ranked_chain() -> LayoutGraph {
let mut g: DiGraph<()> = DiGraph::new();
g.add_node("A", ());
g.add_node("B", ());
g.add_node("C", ());
g.add_edge("A", "B");
g.add_edge("B", "C");
let mut lg = LayoutGraph::from_digraph(&g, |_, _| (10.0, 10.0));
lg.ranks = vec![0, 1, 2]; lg
}
fn make_ranked_diamond() -> LayoutGraph {
let mut g: DiGraph<()> = DiGraph::new();
g.add_node("A", ());
g.add_node("B", ());
g.add_node("C", ());
g.add_node("D", ());
g.add_edge("A", "B");
g.add_edge("A", "C");
g.add_edge("B", "D");
g.add_edge("C", "D");
let mut lg = LayoutGraph::from_digraph(&g, |_, _| (10.0, 10.0));
lg.ranks = vec![0, 1, 1, 2]; lg
}
#[test]
fn test_slack_tight_edge() {
let lg = make_chain_graph();
assert_eq!(slack(&lg, 0), 0);
}
#[test]
fn test_slack_non_tight_edge() {
let lg = make_chain_graph();
assert_eq!(slack(&lg, 1), 1);
}
#[test]
fn test_slack_with_custom_minlen() {
let mut lg = make_chain_graph();
lg.edge_minlens[0] = 2;
assert_eq!(slack(&lg, 0), -1);
}
fn make_simple_ab_tree() -> (LayoutGraph, SpanningTree) {
let mut g: DiGraph<()> = DiGraph::new();
g.add_node("A", ());
g.add_node("B", ());
g.add_edge("A", "B");
let mut lg = LayoutGraph::from_digraph(&g, |_, _| (10.0, 10.0));
lg.ranks = vec![0, 1];
let mut tree = SpanningTree::new(2);
tree.add_node(0); tree.add_edge(0, 1, 0); (lg, tree)
}
fn make_diamond_tree() -> (LayoutGraph, SpanningTree) {
let mut g: DiGraph<()> = DiGraph::new();
g.add_node("A", ());
g.add_node("B", ());
g.add_node("C", ());
g.add_node("D", ());
g.add_edge("A", "B"); g.add_edge("A", "C"); g.add_edge("B", "D"); g.add_edge("C", "D"); let mut lg = LayoutGraph::from_digraph(&g, |_, _| (10.0, 10.0));
lg.ranks = vec![0, 1, 1, 2];
let mut tree = SpanningTree::new(4);
tree.add_node(0); tree.add_edge(0, 1, 0); tree.add_edge(0, 2, 1); tree.add_edge(1, 3, 2); (lg, tree)
}
fn make_exact_cut_value_tree() -> (LayoutGraph, SpanningTree) {
let mut g: DiGraph<()> = DiGraph::new();
g.add_node("A", ());
g.add_node("B", ());
g.add_node("C", ());
g.add_edge("A", "B"); g.add_edge("B", "C"); let mut lg = LayoutGraph::from_digraph(&g, |_, _| (10.0, 10.0));
lg.ranks = vec![0, 1, 2];
let mut tree = SpanningTree::new(3);
tree.add_node(0);
tree.add_edge(0, 1, 0); tree.add_edge(1, 2, 1); (lg, tree)
}
#[test]
fn test_cut_value_simple_edge() {
let (lg, mut tree) = make_simple_ab_tree();
init_low_lim(&mut tree, 0);
init_cut_values(&mut tree, &lg);
assert_eq!(tree.cut_value[1], 1.0);
}
#[test]
fn test_cut_value_diamond() {
let (lg, mut tree) = make_diamond_tree();
init_low_lim(&mut tree, 0);
init_cut_values(&mut tree, &lg);
for node in 0..4 {
if tree.parent[node].is_some() {
assert!(
tree.cut_value[node] >= 0.0,
"node {} has negative cut value {}",
node,
tree.cut_value[node]
);
}
}
}
#[test]
fn test_cut_value_chain_exact() {
let (lg, mut tree) = make_exact_cut_value_tree();
init_low_lim(&mut tree, 0);
init_cut_values(&mut tree, &lg);
assert_eq!(tree.cut_value[2], 1.0);
assert_eq!(tree.cut_value[1], 1.0);
}
#[test]
fn test_low_lim_single_node() {
let mut tree = SpanningTree::new(1);
tree.add_node(0);
init_low_lim(&mut tree, 0);
assert_eq!(tree.low[0], 1);
assert_eq!(tree.lim[0], 1);
}
#[test]
fn test_low_lim_linear_chain() {
let mut tree = SpanningTree::new(3);
tree.in_tree = vec![true; 3];
tree.size = 3;
tree.parent = vec![None, Some(0), Some(1)];
tree.parent_edge = vec![None, Some(0), Some(1)];
init_low_lim(&mut tree, 0);
assert!(is_descendant(&tree, 2, 0)); assert!(is_descendant(&tree, 1, 0)); assert!(is_descendant(&tree, 2, 1)); assert!(!is_descendant(&tree, 0, 1)); assert!(!is_descendant(&tree, 0, 2)); }
#[test]
fn test_low_lim_branching_tree() {
let mut tree = SpanningTree::new(4);
tree.in_tree = vec![true; 4];
tree.size = 4;
tree.parent = vec![None, Some(0), Some(0), Some(1)];
tree.parent_edge = vec![None, Some(0), Some(1), Some(2)];
init_low_lim(&mut tree, 0);
assert!(is_descendant(&tree, 3, 1)); assert!(is_descendant(&tree, 3, 0)); assert!(!is_descendant(&tree, 3, 2)); assert!(!is_descendant(&tree, 2, 1)); }
#[test]
fn test_feasible_tree_linear_chain() {
let mut lg = make_ranked_chain();
let tree = feasible_tree(&mut lg);
assert_eq!(tree.node_count(), 3);
for node in 0..3 {
if let Some(edge_idx) = tree.parent_edge[node] {
assert_eq!(slack(&lg, edge_idx), 0);
}
}
}
#[test]
fn test_feasible_tree_needs_rank_shift() {
let mut g: DiGraph<()> = DiGraph::new();
g.add_node("A", ());
g.add_node("B", ());
g.add_node("C", ());
g.add_node("D", ());
g.add_edge("A", "B");
g.add_edge("B", "D");
g.add_edge("C", "D");
let mut lg = LayoutGraph::from_digraph(&g, |_, _| (10.0, 10.0));
lg.ranks = vec![0, 1, 0, 2];
let tree = feasible_tree(&mut lg);
assert_eq!(tree.size(), 4);
for node in 0..4 {
if let Some(edge_idx) = tree.parent_edge[node] {
assert_eq!(
slack(&lg, edge_idx),
0,
"edge {} has non-zero slack after feasible_tree",
edge_idx
);
}
}
}
#[test]
fn test_feasible_tree_spans_all_nodes() {
let mut lg = make_ranked_diamond();
let tree = feasible_tree(&mut lg);
let tree_edge_count = tree.parent_edge.iter().filter(|e| e.is_some()).count();
assert_eq!(tree_edge_count, 3); }
fn total_edge_length(lg: &LayoutGraph) -> i32 {
let edges = lg.effective_edges();
edges
.iter()
.enumerate()
.map(|(i, &(from, to))| (lg.ranks[to] - lg.ranks[from]) * lg.edge_weights[i] as i32)
.sum()
}
#[test]
fn test_network_simplex_linear_chain() {
let mut g: DiGraph<()> = DiGraph::new();
g.add_node("A", ());
g.add_node("B", ());
g.add_node("C", ());
g.add_edge("A", "B");
g.add_edge("B", "C");
let mut lg = LayoutGraph::from_digraph(&g, |_, _| (10.0, 10.0));
run(&mut lg);
assert_eq!(lg.ranks[lg.node_index[&"A".into()]], 0);
assert_eq!(lg.ranks[lg.node_index[&"B".into()]], 1);
assert_eq!(lg.ranks[lg.node_index[&"C".into()]], 2);
}
#[test]
fn test_network_simplex_diamond() {
let mut g: DiGraph<()> = DiGraph::new();
g.add_node("A", ());
g.add_node("B", ());
g.add_node("C", ());
g.add_node("D", ());
g.add_edge("A", "B");
g.add_edge("A", "C");
g.add_edge("B", "D");
g.add_edge("C", "D");
let mut lg = LayoutGraph::from_digraph(&g, |_, _| (10.0, 10.0));
run(&mut lg);
assert_eq!(lg.ranks[lg.node_index[&"A".into()]], 0);
assert_eq!(lg.ranks[lg.node_index[&"B".into()]], 1);
assert_eq!(lg.ranks[lg.node_index[&"C".into()]], 1);
assert_eq!(lg.ranks[lg.node_index[&"D".into()]], 2);
}
#[test]
fn test_network_simplex_free_floating_source() {
let mut g: DiGraph<()> = DiGraph::new();
g.add_node("A", ());
g.add_node("B", ());
g.add_node("C", ());
g.add_node("D", ());
g.add_node("E", ());
g.add_edge("A", "B");
g.add_edge("B", "C");
g.add_edge("C", "D");
g.add_edge("E", "D");
let mut lg = LayoutGraph::from_digraph(&g, |_, _| (10.0, 10.0));
run(&mut lg);
let e = lg.node_index[&"E".into()];
let d = lg.node_index[&"D".into()];
assert_eq!(
lg.ranks[d] - lg.ranks[e],
1,
"E->D should span exactly 1 rank, E={}, D={}",
lg.ranks[e],
lg.ranks[d]
);
}
#[test]
fn test_network_simplex_total_edge_length_optimal() {
let mut g: DiGraph<()> = DiGraph::new();
g.add_node("A", ());
g.add_node("B", ());
g.add_node("C", ());
g.add_node("D", ());
g.add_node("E", ());
g.add_edge("A", "B");
g.add_edge("B", "C");
g.add_edge("C", "D");
g.add_edge("E", "D");
let mut lg_ns = LayoutGraph::from_digraph(&g, |_, _| (10.0, 10.0));
run(&mut lg_ns);
let total_ns = total_edge_length(&lg_ns);
let mut lg_lp = LayoutGraph::from_digraph(&g, |_, _| (10.0, 10.0));
rank::longest_path(&mut lg_lp);
let total_lp = total_edge_length(&lg_lp);
assert!(
total_ns <= total_lp,
"network simplex ({}) should be <= longest-path ({})",
total_ns,
total_lp
);
}
#[test]
fn test_network_simplex_terminates() {
let mut g: DiGraph<()> = DiGraph::new();
for name in ["A", "B", "C", "D", "E", "F", "G", "H"] {
g.add_node(name, ());
}
g.add_edge("A", "B");
g.add_edge("A", "C");
g.add_edge("B", "D");
g.add_edge("C", "D");
g.add_edge("D", "E");
g.add_edge("D", "F");
g.add_edge("E", "G");
g.add_edge("F", "G");
g.add_edge("G", "H");
let mut lg = LayoutGraph::from_digraph(&g, |_, _| (10.0, 10.0));
run(&mut lg);
let edges = lg.effective_edges();
for (i, &(from, to)) in edges.iter().enumerate() {
assert!(
lg.ranks[to] - lg.ranks[from] >= lg.edge_minlens[i],
"edge {} violates minlen",
i
);
}
}
}