onnx_runtime_shape_inference/
infer.rs1use std::collections::HashMap;
4
5use onnx_runtime_ir::{Dim, Graph, Node, SymbolConstraints, SymbolId, ValueId, WeightRef};
6
7use crate::context::{MergePolicy, NodeIo, SymbolInterner, TypeInfo, TypedShape, merge_shapes};
8use crate::dim_expr::DimExpr;
9use crate::error::ShapeInferError;
10use crate::registry::InferenceRegistry;
11use crate::report::InferenceReport;
12use crate::shape_data::ShapeData;
13
14const ANON_SYMBOL_FLOOR: u32 = 0x8000_0000;
21
22impl InferenceRegistry {
23 pub fn infer_graph(
34 &self,
35 graph: &mut Graph,
36 opset_imports: &HashMap<String, u64>,
37 policy: MergePolicy,
38 ) -> Result<InferenceReport, ShapeInferError> {
39 let order = graph
40 .topological_order()
41 .map_err(|_| ShapeInferError::CycleDetected)?;
42
43 let mut interner = SymbolInterner::new(seed_next_symbol(graph));
44 let mut types: HashMap<ValueId, TypeInfo> = HashMap::new();
45 let mut shape_data: HashMap<ValueId, ShapeData> = HashMap::new();
46
47 seed_sources(graph, &mut types, &mut shape_data);
48
49 let declared_out: HashMap<ValueId, Vec<Dim>> = graph
51 .outputs
52 .iter()
53 .filter_map(|&vid| graph.try_value(vid).map(|v| (vid, v.shape.clone())))
54 .collect();
55
56 for nid in order {
58 let node = graph.node(nid).clone();
59 let inputs = gather_inputs(&node, &types, &shape_data);
60 let outputs = self.infer_node(&node, opset_imports, inputs, policy, &mut interner)?;
61 for (slot, io) in node.outputs.iter().zip(outputs) {
62 if let Some(ti) = io.type_info {
63 types.insert(*slot, ti);
64 }
65 if let Some(sd) = io.shape_data
66 && sd.within_bounds()
67 {
68 shape_data.insert(*slot, sd);
69 }
70 }
71 }
72
73 for (&vid, declared) in &declared_out {
75 if let Some(ti) = types.get(&vid) {
76 let merged = merge_shapes(vid, &ti.shape, declared, policy)?;
77 let dtype = ti.dtype;
78 types.insert(vid, TypeInfo::new(dtype, merged));
79 }
80 }
81
82 let mut resolved = Vec::new();
84 for (&vid, ti) in &types {
85 if graph.try_value(vid).is_none() {
86 continue;
87 }
88 let dims: Vec<Dim> = ti.shape.iter().map(|d| interner.lower(d)).collect();
89 let value = graph.value_mut(vid);
90 value.shape = dims;
91 value.dtype = ti.dtype;
92 resolved.push(vid);
93 }
94
95 for &sym in interner.fresh_symbols() {
97 graph
98 .symbol_constraints
99 .entry(sym)
100 .or_insert_with(|| SymbolConstraints::new(sym, None));
101 }
102
103 let unresolved: Vec<ValueId> = graph
104 .values
105 .keys()
106 .filter(|vid| !types.contains_key(vid))
107 .collect();
108
109 Ok(InferenceReport {
110 total_values: graph.num_values(),
111 fresh_symbols: interner.fresh_symbols().len(),
112 resolved,
113 unresolved,
114 })
115 }
116}
117
118fn seed_sources(
121 graph: &Graph,
122 types: &mut HashMap<ValueId, TypeInfo>,
123 shape_data: &mut HashMap<ValueId, ShapeData>,
124) {
125 for (vid, value) in graph.values.iter() {
126 if value.producer.is_some() {
127 continue;
128 }
129 let shape: TypedShape = value.shape.iter().map(|&d| DimExpr::from(d)).collect();
130 types.insert(vid, TypeInfo::new(value.dtype, shape));
131 }
132 for (&vid, weight) in &graph.initializers {
134 if let WeightRef::Inline(t) = weight
135 && let Some(sd) = ShapeData::from_tensor(t.dtype, &t.dims, &t.data)
136 {
137 shape_data.insert(vid, sd);
138 }
139 }
140}
141
142fn gather_inputs(
144 node: &Node,
145 types: &HashMap<ValueId, TypeInfo>,
146 shape_data: &HashMap<ValueId, ShapeData>,
147) -> Vec<NodeIo> {
148 node.inputs
149 .iter()
150 .map(|slot| match slot {
151 Some(vid) => NodeIo {
152 type_info: types.get(vid).cloned(),
153 shape_data: shape_data.get(vid).cloned(),
154 },
155 None => NodeIo::default(),
156 })
157 .collect()
158}
159
160fn seed_next_symbol(graph: &Graph) -> u32 {
163 let mut max = ANON_SYMBOL_FLOOR.saturating_sub(1);
164 for &SymbolId(id) in graph.symbol_constraints.keys() {
165 max = max.max(id);
166 }
167 for value in graph.values.values() {
168 for dim in &value.shape {
169 if let Dim::Symbolic(SymbolId(id)) = dim {
170 max = max.max(*id);
171 }
172 }
173 }
174 max.saturating_add(1)
175}