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