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 name(&self) -> StaticName {
102 "DynTile".into()
103 }
104
105 op_as_typed_op!();
106}
107
108impl EvalOp for DynTile {
109 op_out_of_plan!();
110
111 fn eval(&self, ctx: &EvalContext, inputs: TVec<TValue>) -> TractResult<TVec<TValue>> {
112 let multipliers = inputs[1].cast_to::<TDim>()?;
113 let multipliers: TVec<usize> = multipliers
114 .try_as_plain()?
115 .as_slice::<TDim>()?
116 .iter()
117 .map(|m| Ok(m.eval_to_i64(ctx.symbols)? as usize))
118 .collect::<TractResult<_>>()?;
119 Ok(tvec!(tile(&inputs[0], &multipliers)?))
120 }
121}
122
123impl TypedOp for DynTile {
124 as_op!();
125
126 fn declutter(
127 &self,
128 model: &TypedModel,
129 node: &TypedNode,
130 ) -> TractResult<Option<TypedModelPatch>> {
131 if let Some(mult) = &model.outlet_fact(node.inputs[1])?.konst {
132 let multipliers = mult
133 .cast_to::<TDim>()?
134 .try_as_plain()?
135 .as_slice::<TDim>()?
136 .iter()
137 .cloned()
138 .collect();
139 return TypedModelPatch::replace_single_op(
140 model,
141 node,
142 &node.inputs,
143 Tile { multipliers },
144 )
145 .map(Some);
146 }
147 Ok(None)
148 }
149
150 fn output_facts(&self, inputs: &[&TypedFact]) -> TractResult<TVec<TypedFact>> {
151 let multipliers = if let Some(k) = &inputs[1].konst {
152 k.cast_to::<TDim>()?.try_as_plain()?.as_slice::<TDim>()?.iter().cloned().collect()
153 } else {
154 self.multiplier_placeholders.clone()
155 };
156 let shape =
157 inputs[0].shape.iter().zip(multipliers).map(|(a, b)| b * a).collect::<TVec<_>>();
158 Ok(tvec!(inputs[0].datum_type.fact(shape)))
159 }
160}
161
162fn tile(data: &TValue, multipliers: &[usize]) -> TractResult<TValue> {
165 ensure!(multipliers.len() == data.rank(), "Tiling {data:?} by {multipliers:?}");
166 let mut current = None;
167 for (axis, &m) in multipliers.iter().enumerate() {
168 let source: &Tensor = current.as_ref().unwrap_or(data);
169 if m == 1 {
170 continue;
171 }
172 let dim = source.shape()[axis];
173 let mut shape: TVec<usize> = source.shape().into();
174 shape[axis] = dim * m;
175 let mut output = Tensor::zero_dt(source.datum_type(), &shape)?;
176 for repeat in 0..m {
177 output.assign_slice(repeat * dim..(repeat + 1) * dim, source, .., axis)?;
178 }
179 current = Some(output);
180 }
181 Ok(current.map(|t| t.into_tvalue()).unwrap_or_else(|| data.clone()))
182}