Skip to main content

tract_core/ops/array/
tile.rs

1use 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}