tract_core/ops/array/
tile.rs1use crate::internal::*;
2use ndarray::*;
3
4use super::MultiBroadcastTo;
5
6#[derive(Debug, Clone, new, Default, Hash, PartialEq, Eq)]
7pub struct Tile {
8 pub multipliers: TVec<TDim>,
9}
10
11impl Op for Tile {
12 fn name(&self) -> StaticName {
13 "Tile".into()
14 }
15
16 fn info(&self) -> TractResult<Vec<String>> {
17 Ok(vec![format!("multipliers: {:?}", self.multipliers)])
18 }
19
20 op_as_typed_op!();
21}
22
23impl EvalOp for Tile {
24 fn is_stateless(&self) -> bool {
25 true
26 }
27
28 fn eval_with_session(
29 &self,
30 _node_id: usize,
31 session: &TurnState,
32 inputs: TVec<TValue>,
33 ) -> TractResult<TVec<TValue>> {
34 let multipliers: TVec<usize> = self
35 .multipliers
36 .iter()
37 .map(|m| m.eval(&session.resolved_symbols).to_usize())
38 .collect::<TractResult<_>>()?;
39 let result =
40 dispatch_datum_by_size!(eval_t(inputs[0].datum_type())(&inputs[0], &multipliers))?;
41 Ok(tvec!(result))
42 }
43}
44
45impl TypedOp for Tile {
46 as_op!();
47
48 fn set_symbols(
49 &self,
50 _source: &TypedModel,
51 node: &TypedNode,
52 target: &mut TypedModel,
53 mapping: &HashMap<OutletId, OutletId>,
54 subs: &HashMap<Symbol, TDim>,
55 ) -> TractResult<TVec<OutletId>> {
56 let multipliers =
57 self.multipliers.iter().map(|m| m.substitute_all(subs)).collect::<TractResult<_>>()?;
58 target.wire_node(&node.name, Self { multipliers }, &[mapping[&node.inputs[0]]])
59 }
60
61 fn declutter(
62 &self,
63 model: &TypedModel,
64 node: &TypedNode,
65 ) -> TractResult<Option<TypedModelPatch>> {
66 let input_fact = model.outlet_fact(node.inputs[0])?;
67 if input_fact
68 .shape
69 .iter()
70 .zip(self.multipliers.iter())
71 .all(|(i, m)| i.is_one() || m.is_one())
72 {
73 let output_fact = self.output_facts(&[input_fact])?.remove(0);
74 TypedModelPatch::replace_single_op(
75 model,
76 node,
77 &node.inputs[0..1],
78 MultiBroadcastTo { shape: output_fact.shape },
79 )
80 .map(Some)
81 } else {
82 Ok(None)
83 }
84 }
85
86 fn output_facts(&self, inputs: &[&TypedFact]) -> TractResult<TVec<TypedFact>> {
87 let shape = inputs[0]
88 .shape
89 .iter()
90 .zip(self.multipliers.iter())
91 .map(|(a, b)| a.clone() * b)
92 .collect::<TVec<_>>();
93 Ok(tvec!(inputs[0].datum_type.fact(shape)))
94 }
95}
96
97#[derive(Debug, Clone, Hash, PartialEq, Eq)]
98pub struct DynTile {
99 pub multiplier_placeholders: TVec<TDim>,
100}
101
102impl DynTile {
103 pub fn new(scope: &SymbolScope, rank: usize) -> DynTile {
104 let multiplier_placeholders =
105 (0..rank).map(|_| scope.new_with_prefix("_tile_mult_").to_dim()).collect();
106 DynTile { multiplier_placeholders }
107 }
108}
109
110impl Op for DynTile {
111 fn name(&self) -> StaticName {
112 "DynTile".into()
113 }
114
115 op_as_typed_op!();
116}
117
118impl EvalOp for DynTile {
119 fn is_stateless(&self) -> bool {
120 true
121 }
122
123 fn eval_with_session(
124 &self,
125 _node_id: usize,
126 session: &TurnState,
127 inputs: TVec<TValue>,
128 ) -> TractResult<TVec<TValue>> {
129 let multipliers = inputs[1].cast_to::<TDim>()?;
130 let multipliers: TVec<usize> = multipliers
131 .try_as_plain()?
132 .as_slice::<TDim>()?
133 .iter()
134 .map(|m| Ok(m.eval_to_i64(&session.resolved_symbols)? as usize))
135 .collect::<TractResult<_>>()?;
136 let result =
137 dispatch_datum_by_size!(eval_t(inputs[0].datum_type())(&inputs[0], &multipliers))?;
138 Ok(tvec!(result))
139 }
140}
141
142impl TypedOp for DynTile {
143 as_op!();
144
145 fn declutter(
146 &self,
147 model: &TypedModel,
148 node: &TypedNode,
149 ) -> TractResult<Option<TypedModelPatch>> {
150 if let Some(mult) = &model.outlet_fact(node.inputs[1])?.konst {
151 let multipliers = mult
152 .cast_to::<TDim>()?
153 .try_as_plain()?
154 .as_slice::<TDim>()?
155 .iter()
156 .cloned()
157 .collect();
158 return TypedModelPatch::replace_single_op(
159 model,
160 node,
161 &node.inputs,
162 Tile { multipliers },
163 )
164 .map(Some);
165 }
166 Ok(None)
167 }
168
169 fn output_facts(&self, inputs: &[&TypedFact]) -> TractResult<TVec<TypedFact>> {
170 let multipliers = if let Some(k) = &inputs[1].konst {
171 k.cast_to::<TDim>()?.try_as_plain()?.as_slice::<TDim>()?.iter().cloned().collect()
172 } else {
173 self.multiplier_placeholders.clone()
174 };
175 let shape =
176 inputs[0].shape.iter().zip(multipliers).map(|(a, b)| b * a).collect::<TVec<_>>();
177 Ok(tvec!(inputs[0].datum_type.fact(shape)))
178 }
179}
180
181fn eval_t<T: Datum>(data: &TValue, multipliers: &[usize]) -> TractResult<TValue> {
182 let data_plain = data.try_as_plain()?;
183 let view = unsafe { data_plain.to_array_view_unchecked::<T>() };
184 let output_shape: TVec<usize> =
185 view.shape().iter().zip(multipliers.iter()).map(|(&d, &m)| d * m).collect();
186 let output = ndarray::ArrayD::from_shape_fn(&*output_shape, |coords| {
187 let coords: TVec<usize> =
188 coords.slice().iter().zip(data.shape().iter()).map(|(&x, &d)| x % d).collect();
189 view[&*coords].clone()
190 });
191 let mut output = output.into_tensor();
192 unsafe {
193 output.set_datum_type(data.datum_type());
194 }
195
196 Ok(output.into_tvalue())
197}