1use std::collections::HashMap;
2
3use tract_core::ops::konst::Const;
4
5use super::factoid::Factoid;
6use super::{InferenceFact, InferenceModel, InferenceNode, InferenceOp};
7use crate::internal::*;
8use crate::prelude::TVec;
9
10pub trait InferenceModelExt {
11 fn set_symbols(&mut self, subs: &HashMap<Symbol, TDim>) -> TractResult<()>;
13
14 fn analyse(&mut self, obstinate: bool) -> TractResult<bool>;
18
19 fn incorporate(self) -> TractResult<InferenceModel>;
21
22 fn missing_type_shape(&self) -> TractResult<Vec<OutletId>>;
26
27 fn eliminate_dead_branches(self) -> TractResult<InferenceModel>;
31
32 fn into_typed(self) -> TractResult<TypedModel>;
34
35 fn into_optimized(self) -> TractResult<TypedModel>;
39}
40
41impl InferenceModelExt for InferenceModel {
42 fn set_symbols(&mut self, subs: &HashMap<Symbol, TDim>) -> TractResult<()> {
43 for node in &mut self.nodes {
44 for output in &mut node.outputs {
45 for dim in &mut output.fact.shape.dims {
46 if let GenericFactoid::Only(dim) = dim {
47 *dim = dim.substitute_all(subs)?;
48 }
49 }
50 }
51 }
52 Ok(())
53 }
54
55 fn analyse(&mut self, obstinate: bool) -> TractResult<bool> {
59 super::analyser::Analyser::new(self).analyse_obstinate(obstinate)
60 }
61
62 fn incorporate(self) -> TractResult<InferenceModel> {
64 let mut model = self;
65 loop {
66 let mut done_something = false;
67 for p in crate::infer::optim::incorporate() {
68 done_something = done_something || p.pass(&mut model)?;
69 if cfg!(debug_assertions) {
70 model.check_edges()?;
71 }
72 }
73 if !done_something {
74 break;
75 }
76 }
77 model = model.into_compact()?;
78 model.analyse(false)?;
79 Ok(model)
80 }
81
82 fn missing_type_shape(&self) -> TractResult<Vec<OutletId>> {
86 Ok(self
87 .eval_order()?
88 .iter()
89 .flat_map(|&node| {
90 self.nodes()[node]
91 .outputs
92 .iter()
93 .enumerate()
94 .map(move |(ix, outlet)| (OutletId::new(node, ix), outlet))
95 })
96 .filter(|(_, o)| !o.fact.datum_type.is_concrete() || !o.fact.shape.is_concrete())
97 .map(|(id, _)| id)
98 .collect())
99 }
100
101 fn eliminate_dead_branches(self) -> TractResult<InferenceModel> {
105 self.into_compact()
106 }
107
108 fn into_typed(mut self) -> TractResult<TypedModel> {
110 use tract_core::internal::translator::Translate;
111
112 self.analyse(false)?;
113 let m = self.incorporate()?;
114
115 #[derive(Debug)]
116 struct ToTypedTranslator;
117 impl Translate<InferenceFact, Box<dyn InferenceOp>, TypedFact, Box<dyn TypedOp>>
118 for ToTypedTranslator
119 {
120 fn translate_node(
121 &self,
122 source: &InferenceModel,
123 node: &InferenceNode,
124 target: &mut TypedModel,
125 mapping: &HashMap<OutletId, OutletId>,
126 ) -> TractResult<TVec<OutletId>> {
127 if !InferenceModel::is_source(&node.op)
131 && node.op.state(&EvalContext::out_of_plan())?.is_none()
132 && source.node_output_facts(node.id)?.iter().all(|f| f.value.is_concrete())
133 {
134 (0..node.outputs.len())
135 .map(|ix| {
136 target.add_const(
137 format!("{}.{}", node.name, ix),
138 node.outputs[ix].fact.value.concretize().unwrap(),
139 )
140 })
141 .collect()
142 } else {
143 let outputs = node.op.to_typed(source, node, target, mapping)?;
144 for output in &outputs {
145 let fact = target.outlet_fact(*output)?;
146 fact.consistent().with_context(|| {
147 format!(
148 "Checking oulet fact consistency for {:?}: {:?} after translating {:?}",
149 output,
150 fact, node.op,
151 )
152 })?;
153 }
154 Ok(outputs)
155 }
156 }
157 }
158
159 ToTypedTranslator.translate_model(&m)
160 }
161
162 fn into_optimized(self) -> TractResult<TypedModel> {
168 self.into_typed()?.into_optimized()
169 }
170}
171
172impl SpecialOps<InferenceFact, Box<dyn InferenceOp>> for InferenceModel {
173 fn is_source(op: &Box<dyn InferenceOp>) -> bool {
174 op.as_op().downcast_ref::<crate::ops::source::Source>().is_some()
175 }
176
177 fn create_dummy(&self) -> Box<dyn InferenceOp> {
178 Box::new(tract_core::ops::dummy::Dummy::new())
179 }
180
181 fn create_source(&self, _fact: InferenceFact) -> Box<dyn InferenceOp> {
182 Box::new(crate::ops::source::Source::new())
183 }
184
185 fn wire_node(
186 &mut self,
187 name: impl Into<String>,
188 op: impl Into<Box<dyn InferenceOp>>,
189 inputs: &[OutletId],
190 ) -> TractResult<TVec<OutletId>> {
191 let op = op.into();
192 let output_facts: TVec<InferenceFact> =
193 (0..op.nboutputs()?).map(|_| InferenceFact::default()).collect();
194 let id = self.add_node(name, op, output_facts)?;
195 inputs
196 .iter()
197 .enumerate()
198 .try_for_each(|(ix, i)| self.add_edge(*i, InletId::new(id, ix)))?;
199 Ok(self.node(id).outputs.iter().enumerate().map(|(ix, _)| OutletId::new(id, ix)).collect())
200 }
201
202 fn add_const(
203 &mut self,
204 name: impl Into<String>,
205 v: impl IntoArcTensor,
206 ) -> TractResult<OutletId> {
207 let v = v.into_arc_tensor();
208 for node in &self.nodes {
209 if let Some(op) = node.op_as::<Const>() {
210 if op.val() == &v {
211 return Ok(node.id.into());
212 }
213 }
214 }
215 let name = name.into();
216 let fact = TypedFact::try_from(v.clone())?;
217 self.add_node(name, crate::ops::konst::Const::new(v)?, tvec!(fact.into()))
218 .map(|id| id.into())
219 }
220}
221
222#[cfg(test)]
223mod test {
224 use super::*;
225
226 #[test]
227 fn test() {
228 fn is_sync<T: Sync>() {}
229 is_sync::<InferenceModel>();
230 }
231}