tract_core/ops/array/
tile.rs

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