xref: /aosp_15_r20/external/armnn/src/backends/reference/workloads/MirrorPad.cpp (revision 89c4ff92f2867872bb9e2354d150bf0c8c502810)
1 //
2 // Copyright © 2021 Arm Ltd and Contributors. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 
6 #include "MirrorPad.hpp"
7 
8 #include "BaseIterator.hpp"
9 #include "Decoders.hpp"
10 #include "Encoders.hpp"
11 
12 namespace
13 {
14 
15 // Convert a linear index into n-dimensional coordinates.
16 // E.g. index = 2 returns [0, 0, 2].
IndexToCoord(const armnn::TensorShape & shape,unsigned int index)17 inline std::vector<unsigned int> IndexToCoord(const armnn::TensorShape& shape, unsigned int index)
18 {
19     unsigned int numOfElements = shape.GetNumElements();
20 
21     ARMNN_ASSERT_MSG(index <= numOfElements, "Index has to be in [0, num_elements]");
22     ARMNN_ASSERT_MSG(numOfElements != 0, "Cannot create coordinate from empty shape");
23 
24     std::vector<unsigned int> coord(shape.GetNumDimensions());
25     for(unsigned int i = 0; i < shape.GetNumDimensions(); ++i)
26     {
27         numOfElements /= shape[i];
28         coord[i] = index / numOfElements;
29         index %= numOfElements;
30     }
31 
32     return coord;
33 }
34 
35 // Returns the index of a given coordinate.
36 // E.g. [0, 0, 2] returns 2.
CoordToIndex(const armnn::TensorShape & shape,const std::vector<unsigned int> & coord)37 inline unsigned int CoordToIndex(const armnn::TensorShape& shape, const std::vector<unsigned int>& coord)
38 {
39     ARMNN_ASSERT_MSG(shape.GetNumDimensions() != 0, "Cannot get index from empty shape");
40     ARMNN_ASSERT_MSG(coord.size() != 0, "Cannot get index of empty coordinate");
41 
42     unsigned int index    = 0;
43     unsigned int dimSize  = 1;
44 
45     for (unsigned int i = shape.GetNumDimensions(); i > 0; --i)
46     {
47         index += coord[i - 1] * dimSize;
48         dimSize *= shape[i - 1];
49     }
50 
51     return index;
52 }
53 
54 } // anonymous namespace
55 
56 namespace armnn
57 {
58 
MirrorPad(const TensorInfo & inputInfo,const TensorInfo & outputInfo,const ITensorHandle * inputHandle,ITensorHandle * outputHandle,const PadQueueDescriptor & data)59 void MirrorPad(const TensorInfo& inputInfo,
60                const TensorInfo& outputInfo,
61                const ITensorHandle* inputHandle,
62                ITensorHandle* outputHandle,
63                const PadQueueDescriptor& data)
64 {
65     auto padList  = data.m_Parameters.m_PadList;
66     PaddingMode paddingMode = data.m_Parameters.m_PaddingMode;
67 
68     TensorShape outputShape = outputInfo.GetShape();
69     TensorShape inputShape  = inputInfo.GetShape();
70 
71     unsigned int numOutputElements = outputInfo.GetNumElements();
72     unsigned int numInputDimensions = inputShape.GetNumDimensions();
73     assert(numInputDimensions == outputShape.GetNumDimensions());
74 
75     // If padding mode is Reflect then both paddings must be no greater than inputShape(i) - 1.
76     // If padding mode is Symmetric then both paddings must be no greater than inputShape(i).
77     const unsigned int isReflect = static_cast<unsigned int>(paddingMode == PaddingMode::Reflect);
78     for(unsigned int i = 0; i < padList.size(); ++i)
79     {
80         if(padList.at(i).first > (inputShape[i] - isReflect) ||
81            padList.at(i).second > (inputShape[i] - isReflect))
82         {
83             throw armnn::InvalidArgumentException("Paddings must be less (Reflect) or "
84                                                   "equal (Symmetric) to the dimension size.");
85         }
86     }
87 
88     auto inputData = MakeDecoder<float>(inputInfo, inputHandle->Map());
89     auto outData   = MakeEncoder<float>(outputInfo, outputHandle->Map());
90 
91     Decoder<float>& input  = *inputData;
92     Encoder<float>& output = *outData;
93 
94     for(unsigned int idx = 0; idx < numOutputElements; ++idx)
95     {
96         // Get the coordinates of the current index in vector form. E.g inx 1 = [0, 0, 0, 1 ]
97         const std::vector<unsigned int> coord = IndexToCoord(outputShape, idx);
98 
99         std::vector<unsigned int> dimensions;
100         std::vector<unsigned int> coords;
101 
102         for(unsigned int i = 0; i < numInputDimensions; ++i)
103         {
104             dimensions.emplace_back(i);
105             coords.emplace_back(coord[i]);
106         }
107 
108         auto isInPadding = [&](unsigned int i)
109         {
110             return (coords[i] < padList[i].first || coords[i] > inputShape[i] + padList[i].first - 1);
111         };
112 
113         auto getReflectIndex = [&](unsigned int i) -> unsigned int
114         {
115             if(isInPadding(i))
116             {
117                 if(coords[i] < padList[i].first)
118                 {
119                     return padList[i].first - coords[i];
120                 }
121                 else
122                 {
123                     return 2 * inputShape[i] + padList[i].first - 2 - coords[i];
124                 }
125             }
126             return coords[i] - padList[i].first;
127         };
128 
129         auto getSymmetricIndex = [&](unsigned int i) -> unsigned int
130         {
131             if(isInPadding(i))
132             {
133                 if(coords[i] < padList[i].first)
134                 {
135                     return padList[i].first - coords[i] - 1;
136                 }
137                 else
138                 {
139                     return 2 * inputShape[i] + padList[i].first - 1 - coords[i];
140                 }
141             }
142             return coords[i] - padList[i].first;
143         };
144 
145         // Location of the value in the input tensor to use in the output.
146         std::vector<unsigned int> coordOfInput;
147 
148         // any_of works as a loop here to check if any of the dimensions are in the padding.
149         // If dimensions is in the padding area, then create the coordinates of the location in the
150         // input tensor to use in the output.
151         // E.g.
152         // Input tensor = [ 1, 2, 3 ], Rank = 1.
153         // Output tensor = [ 2, 1, 2, 3, 1 ] if Reflect or [ 1, 1, 2, 3, 3 ] if Symmetric with a padding of (1, 1).
154         // So it will either return [ 1 ] or [ 0 ] which is used to set the first value in the output tensor and so on.
155         if(std::any_of(dimensions.begin(), dimensions.end(), isInPadding))
156         {
157             switch(paddingMode)
158             {
159                 case PaddingMode::Reflect:
160                 {
161                     for(unsigned int i = 0; i < numInputDimensions; ++i)
162                     {
163                         coordOfInput.emplace_back(getReflectIndex(i));
164                     }
165                     break;
166                 }
167                 case PaddingMode::Symmetric:
168                 {
169                     for(unsigned int i = 0; i < numInputDimensions; ++i)
170                     {
171                         coordOfInput.emplace_back(getSymmetricIndex(i));
172                     }
173                     break;
174                 }
175                 default:
176                     throw InvalidArgumentException("Padding mode not supported.");
177                     break;
178             }
179         }
180         else
181         {
182             for(unsigned int i = 0; i < numInputDimensions; ++i)
183             {
184                 coordOfInput.emplace_back(coord[i] - padList[i].first);
185             }
186         }
187 
188         // Set output value using the coordinate of the input value to use.
189         const unsigned int indexOfInput = CoordToIndex(inputShape, coordOfInput);
190 
191         input[indexOfInput];
192         auto inputValue = input.Get();
193 
194         output[idx];
195         output.Set(inputValue);
196     }
197 }
198 
199 } //namespace armnn