1use crate::internal::*;
2
3#[derive(Debug, Clone, PartialEq, Eq, Hash)]
7pub enum PadMode {
8 Constant(Arc<Tensor>),
9 Reflect,
10 Edge,
11}
12
13impl Default for PadMode {
14 fn default() -> PadMode {
15 PadMode::Constant(Arc::new(0.0f32.into()))
16 }
17}
18
19#[derive(Debug, Clone, new, Default, Hash, PartialEq, Eq)]
20pub struct Pad {
21 pub pads: Vec<(usize, usize)>,
22 pub mode: PadMode,
23}
24
25impl Pad {
26 fn eval_t<T>(&self, input_tensor: TValue) -> TractResult<TValue>
27 where
28 T: Copy + Datum,
29 {
30 use tract_ndarray::*;
31 let input = input_tensor.to_plain_array_view::<T>()?;
32 let output_shape: Vec<usize> =
33 input.shape().iter().zip(self.pads.iter()).map(|(&d, &(a, b))| d + a + b).collect();
34 let element = match &self.mode {
35 PadMode::Constant(f) => f.cast_to_scalar::<T>()?,
36 _ => T::default(),
37 };
38 let mut output = ArrayD::<T>::from_elem(output_shape, element);
39 let slice_spec: Vec<SliceInfoElem> = self
40 .pads
41 .iter()
42 .map(|&(a, b)| SliceInfoElem::Slice {
43 start: a as isize,
44 end: if b != 0 { Some(-(b as isize)) } else { None },
45 step: 1,
46 })
47 .collect();
48 let slice_info = SliceInfo::<_, IxDyn, IxDyn>::try_from(slice_spec).unwrap();
49 output.slice_mut(slice_info.as_ref()).assign(&input);
50 if self.mode == PadMode::Reflect || self.mode == PadMode::Edge {
51 for (ax, &(bef, aft)) in self.pads.iter().enumerate() {
52 let axis = Axis(ax);
53 let dim = output.shape()[ax];
54 {
55 let (mut pad, data) = output.view_mut().split_at(axis, bef);
56 for i in 0..bef {
57 let mut target = pad.slice_axis_mut(axis, Slice::from(i..i + 1));
58 let source_slice = match self.mode {
59 PadMode::Edge => 0,
60 PadMode::Reflect => bef - i,
61 _ => panic!(),
62 };
63 let source =
64 data.slice_axis(axis, Slice::from(source_slice..source_slice + 1));
65 target.assign(&source);
66 }
67 }
68 {
69 let (data, mut pad) = output.view_mut().split_at(axis, dim - aft);
70 for i in 0..aft {
71 let mut target = pad.slice_axis_mut(axis, Slice::from(i..i + 1));
72 let source_slice = match self.mode {
73 PadMode::Edge => dim - aft - 1,
74 PadMode::Reflect => dim - aft - 2 - i,
75 _ => panic!(),
76 };
77 let source =
78 data.slice_axis(axis, Slice::from(source_slice..source_slice + 1));
79 target.assign(&source);
80 }
81 }
82 }
83 }
84 let mut output = output.into_tensor();
85 unsafe { output.set_datum_type(input_tensor.datum_type()) }
86 Ok(output.into_tvalue())
87 }
88}
89
90impl Op for Pad {
91 fn name(&self) -> StaticName {
92 "Pad".into()
93 }
94
95 fn info(&self) -> TractResult<Vec<String>> {
96 Ok(vec![format!("Mode: {:?}, pads: {:?})", self.mode, self.pads,)])
97 }
98
99 op_as_typed_op!();
100}
101
102impl EvalOp for Pad {
103 op_out_of_plan!();
104
105 fn eval(&self, _ctx: &EvalContext, inputs: TVec<TValue>) -> TractResult<TVec<TValue>> {
106 let input = args_1!(inputs);
107 Ok(tvec!(dispatch_numbers!(Self::eval_t(input.datum_type())(self, input))?))
108 }
109}
110
111impl TypedOp for Pad {
112 as_op!();
113
114 fn output_facts(&self, inputs: &[&TypedFact]) -> TractResult<TVec<TypedFact>> {
115 let mut fact = inputs[0].without_value();
116 if self.pads.len() != fact.rank() {
117 bail!("Inconsistent pad: input of rank {}, pads are: {:?}", fact.rank(), self.pads);
118 }
119 for (ix, (b, e)) in self.pads.iter().enumerate() {
120 fact.shape.set(ix, fact.shape[ix].clone() + *b + *e);
121 }
122 Ok(tvec!(fact))
123 }
124
125 fn input_roi(
126 &self,
127 model: &TypedModel,
128 node: &TypedNode,
129 ) -> TractResult<Option<TVec<Option<TDim>>>> {
130 let output_fact = model.outlet_fact(OutletId::new(node.id, 0))?;
131 rule_if_some!(roi = &output_fact.region_of_interest);
132 let mut input_roi = roi.clone();
134 for (axis, &(before, _)) in self.pads.iter().enumerate() {
135 if before == 0 {
136 continue;
137 }
138 if let Some(sym) = input_roi
139 .symbols()
140 .into_iter()
141 .find(|s| crate::ops::logic::sym_to_coord_axis(s) == Some(axis))
142 {
143 let shifted = TDim::Sym(sym.clone()) - TDim::Val(before as i64);
144 input_roi = input_roi.substitute(&sym, &shifted).unwrap_or(input_roi);
145 }
146 }
147 Ok(Some(tvec![Some(input_roi)]))
148 }
149
150 fn axes_mapping(
151 &self,
152 inputs: &[&TypedFact],
153 outputs: &[&TypedFact],
154 ) -> TractResult<AxesMapping> {
155 let mut result = AxesMapping::disconnected(inputs, outputs)?;
156 for (ix, pads) in self.pads.iter().enumerate() {
157 if pads == &(0, 0) {
158 result = result.linking((InOut::In(0), ix), (InOut::Out(0), ix))?;
159 }
160 }
161 Ok(result)
162 }
163
164 fn change_axes(
165 &self,
166 model: &TypedModel,
167 node: &TypedNode,
168 io: InOut,
169 change: &AxisOp,
170 ) -> TractResult<Option<AxisChangeConsequence>> {
171 let mut new_op = self.clone();
172 if let (InOut::In(0), AxisOp::Rm(ix)) = (io, change)
173 && new_op.pads.remove(*ix) == (0, 0)
174 {
175 return Ok(Some(AxisChangeConsequence::new(
176 model,
177 node,
178 Some(Box::new(new_op)),
179 change,
180 )));
181 }
182 if let (InOut::In(0), AxisOp::Add(ix)) = (io, change) {
183 new_op.pads.insert(*ix, (0, 0));
184 return Ok(Some(AxisChangeConsequence::new(
185 model,
186 node,
187 Some(Box::new(new_op)),
188 change,
189 )));
190 }
191 Ok(None)
192 }
193
194 fn declutter(
195 &self,
196 model: &TypedModel,
197 node: &TypedNode,
198 ) -> TractResult<Option<TypedModelPatch>> {
199 if self.pads.iter().all(|p| p.0 == 0 && p.1 == 0) {
200 TypedModelPatch::shunt_one_op(model, node)
201 } else {
202 Ok(None)
203 }
204 }
205}