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