use std::{
cell::RefCell,
collections::{HashMap, HashSet},
rc::Rc,
};
use crate::{
graph_state::GraphState,
ir::{ArgType, RawNode},
processor::{ArgPreference, ProcessError, get_processor_registry},
};
pub(crate) fn infer_types(
state_rc: &Rc<RefCell<GraphState>>,
opset_version: usize,
) -> Result<(), ProcessError> {
let mut nodes = std::mem::take(&mut state_rc.borrow_mut().processed_nodes);
iterative_type_inference_with_preferences(&mut nodes, opset_version)?;
state_rc.borrow_mut().processed_nodes = nodes;
Ok(())
}
pub(super) fn iterative_type_inference_with_preferences(
nodes: &mut [RawNode],
opset: usize,
) -> Result<(), ProcessError> {
let registry = get_processor_registry();
let mut collected_preferences: HashSet<(String, String, String)> = HashSet::new();
let max_iterations = 10;
for iteration in 1..=max_iterations {
let mut node_preferences: HashMap<String, crate::processor::OutputPreferences> =
HashMap::new();
for (output_name, consumer_name, pref_type_str) in &collected_preferences {
let pref = match pref_type_str.as_str() {
"Scalar" => ArgPreference::Scalar,
"Shape" => ArgPreference::Shape,
"Tensor" => ArgPreference::Tensor,
_ => continue,
};
for node in nodes.iter() {
if node.outputs.iter().any(|o| &o.name == output_name) {
node_preferences.entry(node.name.clone()).or_default().add(
output_name.clone(),
consumer_name.clone(),
pref,
);
break;
}
}
}
if iteration > 1 {
let output_types: HashMap<String, ArgType> = nodes
.iter()
.flat_map(|n| n.outputs.iter().map(|o| (o.name.clone(), o.ty.clone())))
.collect();
for node in nodes.iter_mut() {
for input in &mut node.inputs {
if let Some(new_type) = output_types.get(&input.name) {
input.ty = new_type.clone();
}
}
}
}
let mut types_changed = false;
for i in 0..nodes.len() {
let prefs = node_preferences
.get(&nodes[i].name)
.cloned()
.unwrap_or_else(crate::processor::OutputPreferences::new);
let processor = registry.get(&nodes[i].node_type);
let spec = processor.spec();
crate::processor::validate_node_spec(&nodes[i], opset, &spec)?;
processor.infer_types(&mut nodes[i], opset, &prefs)?;
crate::processor::validate_no_rank_zero_tensors(&nodes[i])?;
let current_outputs: Vec<(String, ArgType)> = nodes[i]
.outputs
.iter()
.map(|o| (o.name.clone(), o.ty.clone()))
.collect();
for output_pair in ¤t_outputs {
let (output_name, output_ty) = output_pair;
for downstream_node in &mut nodes[i + 1..] {
for input in &mut downstream_node.inputs {
if &input.name == output_name && input.ty != *output_ty {
types_changed = true;
input.ty = output_ty.clone();
}
}
}
}
}
let output_types: HashMap<String, ArgType> = nodes
.iter()
.flat_map(|n| n.outputs.iter().map(|o| (o.name.clone(), o.ty.clone())))
.collect();
for node in nodes.iter_mut() {
for input in &mut node.inputs {
if let Some(new_type) = output_types.get(&input.name)
&& input.ty != *new_type
{
types_changed = true;
input.ty = new_type.clone();
}
}
}
let mut new_preferences_found = false;
for consumer_node in nodes.iter() {
let processor = registry.get(&consumer_node.node_type);
if let Ok(Some(input_prefs)) = processor.input_preferences(consumer_node, opset) {
for input in &consumer_node.inputs {
let requested_types = input_prefs.get(&input.name);
if requested_types.is_empty() {
continue;
}
for producer_node in nodes.iter() {
if let Some(output) =
producer_node.outputs.iter().find(|o| o.name == input.name)
{
for req_type in requested_types {
let pref_type_str = match req_type {
ArgPreference::Scalar => "Scalar",
ArgPreference::Shape => "Shape",
ArgPreference::Tensor => "Tensor",
}
.to_string();
let key = (
output.name.clone(),
consumer_node.name.clone(),
pref_type_str,
);
if !collected_preferences.contains(&key) {
collected_preferences.insert(key.clone());
new_preferences_found = true;
}
}
break;
}
}
}
}
}
if !types_changed && !new_preferences_found {
log::debug!("Type inference converged after {} iterations", iteration);
return Ok(());
}
}
log::warn!(
"Type inference iteration limit ({}) reached without convergence",
max_iterations
);
Ok(())
}