use std::collections::HashSet;
use crate::ir::{Argument, RawNode};
pub(crate) fn eliminate_dead_nodes(
mut nodes: Vec<RawNode>,
graph_outputs: &[Argument],
) -> Vec<RawNode> {
loop {
let mut live_names: HashSet<String> = HashSet::new();
for output in graph_outputs {
live_names.insert(output.name.clone());
}
for node in &nodes {
for input in &node.inputs {
live_names.insert(input.name.clone());
}
}
let before = nodes.len();
nodes.retain(|node| {
let alive = node
.outputs
.iter()
.any(|out| live_names.contains(&out.name));
if !alive {
log::debug!(
"Dead node elimination: {:?} '{}'",
node.node_type,
node.name,
);
}
alive
});
if nodes.len() == before {
break;
}
}
nodes
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ir::NodeType;
use crate::simplify::tests::{arg, node};
#[test]
fn test_no_dead_nodes() {
let nodes = vec![
node("a", NodeType::Relu, &["input"], &["a_out"]),
node("b", NodeType::Relu, &["a_out"], &["output"]),
];
let outputs = vec![arg("output")];
let result = eliminate_dead_nodes(nodes, &outputs);
assert_eq!(result.len(), 2);
}
#[test]
fn test_single_dead_node() {
let nodes = vec![
node("a", NodeType::Relu, &["input"], &["output"]),
node("b", NodeType::Relu, &["input"], &["unused"]),
];
let outputs = vec![arg("output")];
let result = eliminate_dead_nodes(nodes, &outputs);
assert_eq!(result.len(), 1);
assert_eq!(result[0].name, "a");
}
#[test]
fn test_cascading_dead_nodes() {
let nodes = vec![
node("a", NodeType::Relu, &["input"], &["a_out"]),
node("b", NodeType::Relu, &["a_out"], &["b_out"]),
node("c", NodeType::Relu, &["input"], &["output"]),
];
let outputs = vec![arg("output")];
let result = eliminate_dead_nodes(nodes, &outputs);
assert_eq!(result.len(), 1);
assert_eq!(result[0].name, "c");
}
#[test]
fn test_node_with_multiple_outputs_partially_used() {
let nodes = vec![
node("a", NodeType::Relu, &["input"], &["a_out1", "a_out2"]),
node("b", NodeType::Relu, &["a_out1"], &["output"]),
];
let outputs = vec![arg("output")];
let result = eliminate_dead_nodes(nodes, &outputs);
assert_eq!(result.len(), 2);
}
#[test]
fn test_all_nodes_dead() {
let nodes = vec![
node("a", NodeType::Relu, &["input"], &["a_out"]),
node("b", NodeType::Relu, &["a_out"], &["b_out"]),
];
let outputs = vec![arg("external")];
let result = eliminate_dead_nodes(nodes, &outputs);
assert_eq!(result.len(), 0);
}
}