IVGCVSW-7831: Front end and Reference Implementation for REVERSE_V2

 * Descriptors added for ReverseV2
 * Layer definition added
 * Input validation added
 * Reference workload implementation for ReverseV2 added
 * Reference layer unit tests made for ReverseV2
 * CompareTensors method updated to support comparison between empty tensors
 * CMake and other build files updated

Signed-off-by: Tianle Cheng <tianle.cheng@arm.com>
Change-Id: I805738454421309fda77c44218a8df171d68dc18
diff --git a/src/backends/reference/workloads/ReverseV2Impl.cpp b/src/backends/reference/workloads/ReverseV2Impl.cpp
new file mode 100644
index 0000000..f6d5fd7
--- /dev/null
+++ b/src/backends/reference/workloads/ReverseV2Impl.cpp
@@ -0,0 +1,133 @@
+//
+// Copyright © 2023 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include "ReverseV2Impl.hpp"
+
+#include <armnn/backends/WorkloadData.hpp>
+#include <armnn/Logging.hpp>
+#include <armnnUtils/Permute.hpp>
+
+namespace armnn
+{
+
+// Get multi-dimensional index for input tensor
+std::vector<unsigned int> ReverseGetMultIdx(const unsigned int idx,
+                                            unsigned int inputRank,
+                                            std::vector<unsigned int>& elementNumInner)
+{
+    std::vector<unsigned int> indexList(inputRank);
+
+    unsigned int mIdx = idx;
+
+    for (unsigned int iDim = 0; iDim < inputRank; ++iDim)
+    {
+        indexList[iDim] = static_cast<unsigned int>(mIdx / elementNumInner[iDim]);
+        mIdx %= elementNumInner[iDim];
+    }
+
+    return indexList;
+}
+
+// Get flattened index for output encoder
+unsigned int ReverseGetFlatIdx(const std::vector<unsigned int>& idxList,
+                               unsigned int inputRank,
+                               std::vector<unsigned int>& elementNumInner)
+{
+    unsigned int idx = 0;
+
+    for (unsigned int iDim = 0; iDim < inputRank; ++iDim)
+    {
+        idx += idxList[iDim] * elementNumInner[iDim];
+    }
+
+    return idx;
+}
+
+// Relocate the coordinate to the reversed tensor
+unsigned int ReverseRelocateIdx(unsigned int idx,
+                                unsigned int inputRank,
+                                std::vector<bool>& axisFlag,
+                                std::vector<unsigned int>& dimSize,
+                                std::vector<unsigned int>& elementNumInner)
+{
+    // Get the multidimensional index list for input
+    auto inputIdxList = ReverseGetMultIdx(idx, inputRank, elementNumInner);
+
+    std::vector<unsigned int> outputIdxList(inputRank);
+
+    // Relocate the input index to the output one
+    for (unsigned int iDim = 0; iDim < inputRank; ++iDim)
+    {
+        if (axisFlag[iDim])
+        {
+            outputIdxList[iDim] = dimSize[iDim] - inputIdxList[iDim] - 1;
+        }
+        else
+        {
+            outputIdxList[iDim] = inputIdxList[iDim];
+        }
+    }
+
+    // Get the 1-dimensional flattened index for output
+    unsigned int outputIdx = ReverseGetFlatIdx(outputIdxList, inputRank, elementNumInner);
+    return outputIdx;
+}
+
+void ReverseV2(const ReverseV2Descriptor& params,
+               const TensorInfo& inputInfo,
+               Decoder<float>& inputDecoder,
+               Encoder<float>& outputEncoder)
+{
+    // Empty axis and empty tensor case: copy input to output
+    if (params.m_Axis.empty() || inputInfo.GetNumElements() == 0)
+    {
+        for (unsigned idx = 0; idx < inputInfo.GetNumElements(); idx++)
+        {
+            float inputValue = inputDecoder.Get();
+            inputDecoder += 1;
+            outputEncoder.Set(inputValue);
+            outputEncoder += 1;
+        }
+        return;
+    }
+
+    unsigned int inputRank = static_cast<unsigned int>(inputInfo.GetNumDimensions());
+
+    std::vector<bool>axisFlag(inputRank, false);
+    std::vector<unsigned int>dimSize(inputRank, 0);
+
+    // Make sure the axes are positive
+    for (int32_t axisElement: params.m_Axis)
+    {
+        axisElement = axisElement < 0 ? axisElement + static_cast<int32_t>(inputRank) : axisElement;
+        axisFlag[static_cast<uint32_t>(axisElement)] = true;
+    }
+
+    const TensorShape &inputShape = inputInfo.GetShape();
+
+    unsigned int elementNum = inputInfo.GetNumElements();
+    unsigned int baseDimSize = 1;
+
+    std::vector<unsigned int> elementNumInner;
+
+    // Get the number of element within the specific dimension
+    for (unsigned int iDim = 0; iDim < inputRank; ++iDim) {
+        dimSize[iDim] = inputShape[iDim];
+        baseDimSize *= dimSize[iDim];
+        elementNumInner.push_back(static_cast<unsigned int>(elementNum / baseDimSize));
+    }
+
+    // Iterate through all elements
+    for (unsigned int idx = 0; idx < elementNum; ++idx)
+    {
+        float inputValue = inputDecoder.Get();
+        inputDecoder += 1;
+        auto outputIdx = ReverseRelocateIdx(idx, inputRank, axisFlag, dimSize, elementNumInner);
+        outputEncoder[outputIdx];
+        outputEncoder.Set(inputValue);
+    }
+}
+
+} // namespace armnn
\ No newline at end of file