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 analyse(&mut self, obstinate: bool) -> TractResult<bool>;
15
16 fn incorporate(self) -> TractResult<InferenceModel>;
18
19 fn missing_type_shape(&self) -> TractResult<Vec<OutletId>>;
23
24 fn eliminate_dead_branches(self) -> TractResult<InferenceModel>;
28
29 fn into_typed(self) -> TractResult<TypedModel>;
31
32 fn into_optimized(self) -> TractResult<TypedModel>;
36}
37
38impl InferenceModelExt for InferenceModel {
39 fn analyse(&mut self, obstinate: bool) -> TractResult<bool> {
43 super::analyser::Analyser::new(self).analyse_obstinate(obstinate)
44 }
45
46 fn incorporate(self) -> TractResult<InferenceModel> {
48 let mut model = self;
49 loop {
50 let mut done_something = false;
51 for p in crate::infer::optim::incorporate() {
52 done_something = done_something || p.pass(&mut model)?;
53 if cfg!(debug_assertions) {
54 model.check_edges()?;
55 }
56 }
57 if !done_something {
58 break;
59 }
60 }
61 model = model.into_compact()?;
62 model.analyse(false)?;
63 Ok(model)
64 }
65
66 fn missing_type_shape(&self) -> TractResult<Vec<OutletId>> {
70 Ok(self
71 .eval_order()?
72 .iter()
73 .flat_map(|&node| {
74 self.nodes()[node]
75 .outputs
76 .iter()
77 .enumerate()
78 .map(move |(ix, outlet)| (OutletId::new(node, ix), outlet))
79 })
80 .filter(|(_, o)| !o.fact.datum_type.is_concrete() || !o.fact.shape.is_concrete())
81 .map(|(id, _)| id)
82 .collect())
83 }
84
85 fn eliminate_dead_branches(self) -> TractResult<InferenceModel> {
89 self.into_compact()
90 }
91
92 fn into_typed(mut self) -> TractResult<TypedModel> {
94 use tract_core::internal::translator::Translate;
95
96 self.analyse(false)?;
97 let m = self.incorporate()?;
98
99 #[derive(Debug)]
100 struct ToTypedTranslator;
101 impl Translate<InferenceFact, Box<dyn InferenceOp>, TypedFact, Box<dyn TypedOp>>
102 for ToTypedTranslator
103 {
104 fn translate_node(
105 &self,
106 source: &InferenceModel,
107 node: &InferenceNode,
108 target: &mut TypedModel,
109 mapping: &HashMap<OutletId, OutletId>,
110 ) -> TractResult<TVec<OutletId>> {
111 if !InferenceModel::is_source(&node.op)
115 && node.op.state(&EvalContext::out_of_plan())?.is_none()
116 && source.node_output_facts(node.id)?.iter().all(|f| f.value.is_concrete())
117 {
118 (0..node.outputs.len())
119 .map(|ix| {
120 target.add_const(
121 format!("{}.{}", node.name, ix),
122 node.outputs[ix].fact.value.concretize().unwrap(),
123 )
124 })
125 .collect()
126 } else {
127 let outputs = node.op.to_typed(source, node, target, mapping)?;
128 for output in &outputs {
129 let fact = target.outlet_fact(*output)?;
130 fact.consistent().with_context(|| {
131 format!(
132 "Checking oulet fact consistency for {:?}: {:?} after translating {:?}",
133 output,
134 fact, node.op,
135 )
136 })?;
137 }
138 Ok(outputs)
139 }
140 }
141 }
142
143 ToTypedTranslator.translate_model(&m)
144 }
145
146 fn into_optimized(self) -> TractResult<TypedModel> {
152 self.into_typed()?.into_optimized()
153 }
154}
155
156impl SpecialOps<InferenceFact, Box<dyn InferenceOp>> for InferenceModel {
157 fn is_source(op: &Box<dyn InferenceOp>) -> bool {
158 op.as_op().downcast_ref::<crate::ops::source::Source>().is_some()
159 }
160
161 fn create_dummy(&self) -> Box<dyn InferenceOp> {
162 Box::new(tract_core::ops::dummy::Dummy::new())
163 }
164
165 fn create_source(&self, _fact: InferenceFact) -> Box<dyn InferenceOp> {
166 Box::new(crate::ops::source::Source::new())
167 }
168
169 fn wire_node(
170 &mut self,
171 name: impl Into<String>,
172 op: impl Into<Box<dyn InferenceOp>>,
173 inputs: &[OutletId],
174 ) -> TractResult<TVec<OutletId>> {
175 let op = op.into();
176 let output_facts: TVec<InferenceFact> =
177 (0..op.nboutputs()?).map(|_| InferenceFact::default()).collect();
178 let id = self.add_node(name, op, output_facts)?;
179 inputs
180 .iter()
181 .enumerate()
182 .try_for_each(|(ix, i)| self.add_edge(*i, InletId::new(id, ix)))?;
183 Ok(self.node(id).outputs.iter().enumerate().map(|(ix, _)| OutletId::new(id, ix)).collect())
184 }
185
186 fn add_const(
187 &mut self,
188 name: impl Into<String>,
189 v: impl IntoArcTensor,
190 ) -> TractResult<OutletId> {
191 let v = v.into_arc_tensor();
192 for node in &self.nodes {
193 if let Some(op) = node.op_as::<Const>() {
194 if op.val() == &v {
195 return Ok(node.id.into());
196 }
197 }
198 }
199 let name = name.into();
200 let fact = TypedFact::try_from(v.clone())?;
201 self.add_node(name, crate::ops::konst::Const::new(v)?, tvec!(fact.into()))
202 .map(|id| id.into())
203 }
204}
205
206#[cfg(test)]
207mod test {
208 use super::*;
209
210 #[test]
211 fn test() {
212 fn is_sync<T: Sync>() {}
213 is_sync::<InferenceModel>();
214 }
215}