use crate::adt::dag::NodeHandle;
use crate::adt::dag::DAG;
use crate::core::base::Direction;
#[derive(Debug)]
pub struct EdgeCrossOptimizer<'a> {
dag: &'a mut DAG,
}
impl<'a> EdgeCrossOptimizer<'a> {
pub fn new(dag: &'a mut DAG) -> Self {
Self { dag }
}
fn num_crossing(
&self,
a: NodeHandle,
b: NodeHandle,
row: &[NodeHandle],
) -> usize {
let mut sum = 0;
let mut num_b = 0;
let a_edges1 = self.dag.successors(a);
let a_edges2 = self.dag.predecessors(a);
let b_edges1 = self.dag.successors(b);
let b_edges2 = self.dag.predecessors(b);
for node in row {
let is_a1 = a_edges1.iter().any(|x| x == node);
let is_a2 = a_edges2.iter().any(|x| x == node);
let is_b1 = b_edges1.iter().any(|x| x == node);
let is_b2 = b_edges2.iter().any(|x| x == node);
if is_a1 || is_a2 {
sum += num_b;
}
if is_b1 || is_b2 {
num_b += 1;
}
}
sum
}
pub fn perturb_rank(&mut self) {
for i in 0..self.dag.num_levels() {
let row = self.dag.row_mut(i);
let len = row.len();
for j in 0..len {
row.swap((j * 17) % len, j);
}
}
}
pub fn rotate_rank(&mut self) {
for i in 0..self.dag.num_levels() {
let row = self.dag.row_mut(i);
row.rotate_left(1);
}
}
pub fn optimize(&mut self) {
self.dag.verify();
#[cfg(feature = "log")]
log::info!("Optimizing edge crossing.");
let mut best_rank = self.dag.ranks().clone();
let mut best_cnt = self.count_crossed_edges();
#[cfg(feature = "log")]
log::info!("Starting with {} crossings.", best_cnt);
for i in 0..50 {
let dir = match i % 4 {
0 => Direction::Both,
1 => Direction::Up,
_ => Direction::Down,
};
self.swap_crossed_edges(dir);
let new_cnt = self.count_crossed_edges();
if new_cnt < best_cnt {
#[cfg(feature = "log")]
log::info!("Found a rank with {} crossings.", new_cnt);
best_rank = self.dag.ranks().clone();
best_cnt = new_cnt;
}
self.rotate_rank();
if i % 10 == 0 {
self.perturb_rank();
}
}
*self.dag.ranks_mut() = best_rank;
}
fn count_crossed_edges(&self) -> usize {
let mut sum = 0;
for row_idx in 0..self.dag.num_levels() - 1 {
let first_row = self.dag.row(row_idx);
let second_row = self.dag.row(row_idx + 1);
sum += self.count_crossing_in_rows(first_row, second_row);
}
sum
}
fn count_crossing_in_rows(
&self,
first: &[NodeHandle],
second: &[NodeHandle],
) -> usize {
if first.len() < 2 {
return 0;
}
let mut sum = 0;
for i in 0..first.len() {
for j in i + 1..first.len() {
let a = first[i];
let b = first[j];
sum += self.num_crossing(a, b, second);
}
}
sum
}
fn swap_crossed_edges(&mut self, dir: Direction) {
let mut changed = true;
while changed {
changed = false;
if dir.is_down() {
for i in 0..self.dag.num_levels() {
changed |= self.swap_crossed_edges_on_row(i, dir);
}
}
if dir.is_up() {
for i in (0..self.dag.num_levels()).rev() {
changed |= self.swap_crossed_edges_on_row(i, dir);
}
}
}
}
fn swap_crossed_edges_on_row(
&mut self,
row_idx: usize,
dir: Direction,
) -> bool {
let mut changed = false;
let num_rows = self.dag.num_levels();
let prev_row = if row_idx > 0 && dir.is_up() {
self.dag.row(row_idx - 1).clone()
} else {
Vec::new()
};
let next_row = if row_idx + 1 < num_rows && dir.is_down() {
self.dag.row(row_idx + 1).clone()
} else {
Vec::new()
};
let mut row = self.dag.row(row_idx).clone();
if row.len() < 2 {
return false;
}
for i in 0..row.len() - 1 {
let a = row[i];
let b = row[i + 1];
let mut ab = 0;
let mut ba = 0;
ab += self.num_crossing(a, b, &prev_row);
ba += self.num_crossing(b, a, &prev_row);
ab += self.num_crossing(a, b, &next_row);
ba += self.num_crossing(b, a, &next_row);
if ab > ba {
row[i] = b;
row[i + 1] = a;
changed = true;
}
}
if changed {
*self.dag.row_mut(row_idx) = row;
}
changed
}
}
#[derive(Debug)]
pub struct RankOptimizer<'a> {
dag: &'a mut DAG,
}
impl<'a> RankOptimizer<'a> {
pub fn new(dag: &'a mut DAG) -> Self {
Self { dag }
}
pub fn try_to_sink_node(&mut self, node: NodeHandle) -> bool {
let backs = self.dag.predecessors(node);
let fwds = self.dag.successors(node);
if backs.len() > fwds.len() || backs.len() + fwds.len() == 0 {
return false;
}
let curr_rank = self.dag.level(node);
let mut highest_next = self.dag.len();
for elem in fwds {
let next_rank = self.dag.level(*elem);
highest_next = highest_next.min(next_rank);
}
if highest_next > curr_rank + 1 {
self.dag
.update_node_rank_level(node, highest_next - 1, None);
return true;
}
false
}
pub fn optimize(&mut self) {
self.dag.verify();
#[cfg(feature = "log")]
log::info!("Optimizing the ranks.");
#[cfg(feature = "log")]
let mut cnt = 0;
#[cfg(feature = "log")]
let mut iter = 0;
loop {
let mut c = 0;
for node in self.dag.iter() {
if self.try_to_sink_node(node) {
c += 1;
}
}
#[cfg(feature = "log")]
{
cnt += c;
iter += 1;
}
if c == 0 {
break;
}
}
#[cfg(feature = "log")]
log::info!("Sank {} nodes in {} iteration.", cnt, iter);
}
}