1use super::*;
2use tract_data::internal::*;
3
4#[derive(Debug, Clone, new)]
5pub struct ScanOpParams {
6 pub skip: usize,
7 pub reset_every_turn: bool,
8 pub plan: Arc<TypedSimplePlan>,
9 pub input_mapping: Vec<InputMapping>,
10 pub output_mapping: Vec<OutputMapping<TDim>>,
11}
12
13#[derive(Debug, Clone, new)]
14pub struct OptScan(Arc<ScanOpParams>);
15
16impl std::ops::Deref for OptScan {
17 type Target = ScanOpParams;
18 fn deref(&self) -> &ScanOpParams {
19 &self.0
20 }
21}
22
23impl PartialEq for OptScan {
24 fn eq(&self, _other: &Self) -> bool {
25 false
26 }
27}
28impl Eq for OptScan {}
29
30impl OptScan {
31 pub fn iteration_count(&self, inputs: &[&TypedFact]) -> Option<TDim> {
32 super::iteration_count(&self.input_mapping, inputs)
33 }
34}
35
36impl Op for OptScan {
37 fn mints_runtime_symbols(&self) -> bool {
38 true
39 }
40
41 fn required_bound_symbols(&self, inputs: &[&TypedFact]) -> TVec<Symbol> {
42 let mut syms = super::decluttered::scan_required_bound_symbols(&self.0.plan.model);
43 for fact in inputs {
44 for t in [fact.konst.as_deref(), fact.uniform.as_deref()].into_iter().flatten() {
45 if t.datum_type() == TDim::datum_type()
46 && let Some(slice) =
47 t.try_as_plain_ram().ok().and_then(|v| v.as_slice::<TDim>().ok())
48 {
49 syms.extend(slice.iter().flat_map(|d| d.symbols()));
50 }
51 }
52 }
53 syms
54 }
55
56 fn name(&self) -> StaticName {
57 "Scan".into()
58 }
59
60 fn info(&self) -> TractResult<Vec<String>> {
61 let mut lines = vec![];
62 for (ix, im) in self.input_mapping.iter().enumerate() {
63 lines.push(format!("Model input #{ix}: {im:?}"));
64 }
65 for (ix, om) in self.output_mapping.iter().enumerate() {
66 lines.push(format!("Model output #{ix}: {om:?}"));
67 }
68 Ok(lines)
69 }
70
71 op_as_typed_op!();
72}
73
74impl EvalOp for OptScan {
75 not_out_of_plan!();
76
77 fn state(&self, _ctx: &EvalContext) -> TractResult<Option<Box<dyn OpState>>> {
78 Ok(Some(Box::new(State {
79 position: 0,
80 hidden_state: tvec!(),
81 model_state: self.plan.spawn()?,
82 op: Arc::clone(&self.0),
83 })))
84 }
85}
86
87#[derive(Clone, Debug)]
88pub struct State {
89 op: Arc<ScanOpParams>,
90 position: usize,
91 hidden_state: TVec<TValue>,
92 pub model_state: TypedSimpleState,
93}
94
95impl State {
96 pub fn iteration_count(&self, inputs: &TVec<TValue>) -> usize {
97 let (slot, info) = self
98 .op
99 .input_mapping
100 .iter()
101 .enumerate()
102 .find_map(|(ix, it)| it.as_scan().map(|scan| (ix, scan)))
103 .unwrap();
104 inputs[slot].shape()[info.axis].divceil(info.chunk.unsigned_abs())
105 }
106
107 pub(super) fn slice_input(
108 input: &Tensor,
109 axis: usize,
110 chunk_ix: usize,
111 chunk_dim: isize,
112 ) -> TractResult<Tensor> {
113 unsafe {
114 let full_len = input.shape()[axis];
115 let mut shape: TVec<usize> = input.shape().into();
116 shape[axis] = chunk_dim.unsigned_abs();
117 let mut t = Tensor::uninitialized_dt(input.datum_type(), &shape)?;
118 if chunk_dim < 0 {
119 let chunk_dim = (-chunk_dim) as usize;
120 for i in 0..chunk_dim {
121 if chunk_dim * chunk_ix + i < full_len {
122 let dst_ix = chunk_dim - i - 1;
123 let src_ix = full_len - 1 - (chunk_ix * chunk_dim + i);
124 t.assign_slice_unchecked(dst_ix..=dst_ix, input, src_ix..=src_ix, axis);
125 }
126 }
127 } else if (chunk_ix + 1) * chunk_dim as usize > full_len {
128 let chunk_dim = chunk_dim as usize;
129 let remain = full_len - chunk_ix * chunk_dim;
130 let mut shape: TVec<usize> = input.shape().into();
131 shape[axis] = chunk_dim;
132 t.assign_slice_unchecked(..remain, input, chunk_ix * chunk_dim.., axis);
133 } else {
134 let start = chunk_dim as usize * chunk_ix;
135 let end = start + chunk_dim as usize;
136 t.assign_slice_unchecked(.., input, start..end, axis);
137 }
138 Ok(t)
139 }
140 }
141
142 pub(super) fn assign_output(
143 output: &mut Tensor,
144 axis: usize,
145 element_value: &Tensor,
146 i: usize,
147 backward: bool,
148 ) {
149 let full_len = output.shape()[axis];
150 let offset = if backward {
151 full_len - 1 - i * element_value.shape()[axis]
152 } else {
153 i * element_value.shape()[axis]
154 };
155 let count = element_value.shape()[axis].min(output.shape()[axis] - offset);
156 unsafe {
157 output.assign_slice_unchecked(offset..offset + count, element_value, ..count, axis)
158 };
159 }
160}
161
162impl OpState for State {
163 fn eval(
164 &mut self,
165 ctx: &EvalContext,
166 _op: &dyn Op,
167 inputs: TVec<TValue>,
168 ) -> TractResult<TVec<TValue>> {
169 let iters = self.iteration_count(&inputs);
170
171 let &mut State { ref op, ref mut hidden_state, ref mut position, ref mut model_state } =
172 self;
173
174 if op.reset_every_turn {
176 hidden_state.clear()
177 }
178 if hidden_state.len() == 0 {
179 for (slot, input) in op.input_mapping.iter().enumerate() {
180 if input.is_state() {
181 hidden_state.push(inputs[slot].clone());
182 }
183 }
184 }
185
186 let runs_now = *position + 1 > op.skip;
190 let mut single_shot: TVec<bool> = tvec!();
191 let mut outputs = tvec!();
192 for (ix, output) in op.output_mapping.iter().enumerate() {
193 let mut one_shot = false;
194 if let Some((slot, info)) = output.scan {
195 let fact = op.plan.model().output_fact(ix)?;
196 let mut shape: TVec<usize> = fact.shape.eval_to_usize(ctx.symbols)?.into_owned();
197 let scanning_dim = output
198 .full_dim_hint
199 .as_ref()
200 .and_then(|d| d.as_usize())
201 .unwrap_or(shape[info.axis] * iters);
202 one_shot = iters == 1 && runs_now && scanning_dim == shape[info.axis];
203 shape[info.axis] = scanning_dim;
204 let t = if one_shot {
205 Tensor::default()
206 } else {
207 unsafe { Tensor::uninitialized_dt(fact.datum_type, &shape)? }
208 };
209 outputs.push((slot, t));
210 }
211 single_shot.push(one_shot);
212 if let Some(slot) = output.last_value_slot {
213 outputs.push((slot, Tensor::default()));
214 }
215 }
216 outputs.sort_by_key(|a| a.0);
217 let mut outputs: TVec<Tensor> = outputs.into_iter().map(|(_slot, v)| v).collect();
218
219 model_state.clear_resolved_symbols();
225 let mut iter_inputs: TVec<TValue> = tvec!();
226 let mut symbols_resolved = false;
227
228 for i in 0..iters {
229 *position += 1;
230 if *position <= op.skip {
231 continue;
232 }
233 hidden_state.reverse();
234
235 iter_inputs.clear();
236 for (slot, m) in op.input_mapping.iter().enumerate() {
237 iter_inputs.push(match m {
238 InputMapping::State => hidden_state.pop().unwrap(),
239 InputMapping::Scan(info) => {
240 let input = &inputs[slot];
241 if i == 0 && input.shape()[info.axis] == info.chunk.unsigned_abs() {
244 input.clone()
245 } else {
246 Self::slice_input(input, info.axis, i, info.chunk)?.into_tvalue()
247 }
248 }
249 InputMapping::Full => inputs[slot].clone(),
250 });
251 }
252 trace!("iter_inputs #{i}: {iter_inputs:?}");
253
254 model_state.set_inputs_drain(&mut iter_inputs).context("Setting body inputs")?;
258 if !symbols_resolved {
259 model_state.resolve_symbols_with_states()?;
260 symbols_resolved = true;
261 }
262 model_state.exec().with_context(|| "Evaluating inner body")?;
263 let iter_outputs = model_state.outputs()?;
264 model_state.reset_turn_keep_symbols();
265 trace!("iter_outputs #{i}: {iter_outputs:?}");
266
267 for (ix, (v, mapping)) in iter_outputs.into_iter().zip(&op.output_mapping).enumerate() {
268 if let Some((slot, info)) = mapping.scan {
269 if single_shot[ix] && !mapping.state && mapping.last_value_slot.is_none() {
270 outputs[slot] = v.into_tensor();
271 continue;
272 } else if single_shot[ix] {
273 outputs[slot] = v.clone().into_tensor();
274 } else {
275 Self::assign_output(&mut outputs[slot], info.axis, &v, i, info.chunk < 0);
276 }
277 }
278 if i == iters - 1
279 && let Some(slot) = mapping.last_value_slot
280 {
281 outputs[slot] = v.clone().into_tensor();
282 }
283 if mapping.state {
284 hidden_state.push(v);
285 }
286 }
287 }
288
289 Ok(outputs.into_iter().map(|t| t.into_tvalue()).collect())
290 }
291
292 fn reset_lanes(&mut self, _lanes: &[LaneId]) -> TractResult<()> {
293 bail!("Scan is not lane-aware: its body is a nested state")
294 }
295}
296
297impl TypedOp for OptScan {
298 as_op!();
299
300 fn output_facts(&self, inputs: &[&TypedFact]) -> TractResult<TVec<TypedFact>> {
301 let mut outputs = tvec!();
302 let iters = super::iteration_count(&self.input_mapping, inputs).unwrap();
303 for (ix, output) in self.output_mapping.iter().enumerate() {
304 let fact = self.plan.model().output_fact(ix)?;
305 if let Some(slot) = output.last_value_slot {
306 outputs.push((slot, fact.datum_type.fact(fact.shape.clone())));
307 }
308 if let Some((slot, info)) = output.scan {
309 let mut shape = fact.shape.clone();
310 let scanning_dim =
311 output.full_dim_hint.clone().unwrap_or(shape[info.axis].clone() * &iters);
312 shape.set(info.axis, scanning_dim);
313 outputs.push((slot, fact.datum_type.fact(shape)));
314 }
315 }
316 outputs.sort_by_key(|a| a.0);
317 let outputs: TVec<_> = outputs.into_iter().map(|(_slot, v)| v).collect();
318 Ok(outputs)
319 }
320
321 fn nested_model_multipliers(&self, inputs: &[&TypedFact]) -> Vec<(StaticName, TDim)> {
322 vec![(
323 "loop".into(),
324 super::iteration_count(&self.input_mapping, inputs).unwrap_or_else(|| 1.to_dim()),
325 )]
326 }
327}