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