use std::collections::HashSet;
use super::argument::Argument;
use super::node::{Node, RawNode};
use crate::ir::ValueSource;
use crate::tensor_store::ValueStore;
#[derive(Debug, Clone, Default)]
pub struct OnnxGraph {
pub nodes: Vec<Node>,
pub inputs: Vec<Argument>,
pub outputs: Vec<Argument>,
pub(crate) value_store: Option<ValueStore>,
}
#[derive(Debug, Clone)]
pub struct OnnxGraphBuilder {
pub nodes: Vec<RawNode>,
pub inputs: Vec<Argument>,
pub outputs: Vec<Argument>,
pub(crate) graph_state: Option<std::rc::Rc<std::cell::RefCell<crate::graph_state::GraphState>>>,
}
impl OnnxGraphBuilder {
pub fn convert_to_graph(mut self, opset: usize) -> OnnxGraph {
let value_store = self
.graph_state
.as_ref()
.map(|gs| gs.borrow().build_value_store());
let mut nodes = convert_builders_to_nodes(std::mem::take(&mut self.nodes), opset);
if let Some(ref vs) = value_store {
finalize_arguments_in_nodes(&mut nodes, vs);
for input in &mut self.inputs {
input.set_value_store(vs.clone());
}
for output in &mut self.outputs {
output.set_value_store(vs.clone());
}
}
let mut graph = OnnxGraph {
nodes,
inputs: self.inputs,
outputs: self.outputs,
value_store,
};
eliminate_dead_constants(&mut graph);
graph
}
}
fn finalize_arguments_in_nodes(nodes: &mut [Node], value_store: &ValueStore) {
for node in nodes {
for arg in node.inputs_mut() {
let should_preserve = if let crate::ir::ValueSource::Static(data_id) = arg.value_source
{
arg.value_store
.as_ref()
.map(|store| store.get_tensor_data(data_id).is_some())
.unwrap_or(false)
} else {
false
};
if !should_preserve {
arg.set_value_store(value_store.clone());
}
}
for arg in node.outputs_mut() {
arg.set_value_store(value_store.clone());
}
finalize_subgraphs_in_node(node);
}
}
fn finalize_subgraphs_in_node(node: &mut Node) {
match node {
Node::If(n) => {
finalize_subgraph(&mut n.config.then_branch);
finalize_subgraph(&mut n.config.else_branch);
}
Node::Loop(n) => {
finalize_subgraph(&mut n.config.body);
}
Node::Scan(n) => {
finalize_subgraph(&mut n.config.body);
}
_ => {}
}
}
fn finalize_subgraph(graph: &mut OnnxGraph) {
if let Some(ref vs) = graph.value_store {
let value_store = vs.clone();
finalize_arguments_in_nodes(&mut graph.nodes, &value_store);
for input in &mut graph.inputs {
let should_preserve =
if let crate::ir::ValueSource::Static(data_id) = input.value_source {
input
.value_store
.as_ref()
.map(|store| store.get_tensor_data(data_id).is_some())
.unwrap_or(false)
} else {
false
};
if !should_preserve {
input.set_value_store(value_store.clone());
}
}
for output in &mut graph.outputs {
output.set_value_store(value_store.clone());
}
}
}
fn convert_builders_to_nodes(builders: Vec<RawNode>, opset: usize) -> Vec<Node> {
let registry = crate::processor::get_processor_registry();
builders
.into_iter()
.map(|builder| {
let processor = registry.get(&builder.node_type);
log::debug!(
"Converting node '{}' of type {:?}",
builder.name,
builder.node_type
);
processor.build_node(builder, opset)
})
.collect()
}
fn collect_constant_refs(graph: &OnnxGraph, refs: &mut HashSet<String>) {
for node in &graph.nodes {
let outer_scope_count = match node {
Node::If(n) => n.config.scope_ref_names.len(),
Node::Loop(n) => n.config.scope_ref_names.len(),
Node::Scan(n) => n.config.scope_ref_names.len(),
_ => 0,
};
let inputs_to_check = node.inputs().len().saturating_sub(outer_scope_count);
for input in node.inputs().iter().take(inputs_to_check) {
if input.value_source == ValueSource::Constant && !input.name.is_empty() {
refs.insert(input.name.clone());
}
}
match node {
Node::If(n) => {
collect_constant_refs(&n.config.then_branch, refs);
collect_constant_refs(&n.config.else_branch, refs);
}
Node::Loop(n) => {
collect_constant_refs(&n.config.body, refs);
}
Node::Scan(n) => {
collect_constant_refs(&n.config.body, refs);
}
_ => {}
}
}
for output in &graph.outputs {
if output.value_source == ValueSource::Constant && !output.name.is_empty() {
refs.insert(output.name.clone());
}
}
}
fn eliminate_dead_constants(graph: &mut OnnxGraph) {
let mut referenced = HashSet::new();
collect_constant_refs(graph, &mut referenced);
log::debug!(
"Dead constant elimination: {} referenced constants",
referenced.len()
);
let before_count = graph.nodes.len();
graph.nodes.retain(|node| {
if let Node::Constant(c) = node {
let output_name = c.outputs.first().map(|o| o.name.as_str()).unwrap_or("");
let keep = referenced.contains(output_name);
if !keep {
log::debug!(
"Removing dead constant: {} (output: {})",
c.name,
output_name
);
}
keep
} else {
true
}
});
let removed = before_count - graph.nodes.len();
if removed > 0 {
log::info!("Removed {} dead constants", removed);
}
}