IVGCVSW-7886 Add TILE to delegate and opaque delegate

  * Adding support for Tile in classic and opaque delegates
  * CMake files updated
  * Tests added

Signed-off-by: Tianle Cheng <tianle.cheng@arm.com>
Change-Id: I9b52cea3480eb71961cbccb1a346805f73b5661a
diff --git a/delegate/test/TileTest.cpp b/delegate/test/TileTest.cpp
new file mode 100644
index 0000000..2e20859
--- /dev/null
+++ b/delegate/test/TileTest.cpp
@@ -0,0 +1,91 @@
+//
+// Copyright © 2023 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include "TileTestHelper.hpp"
+
+#include <armnn_delegate.hpp>
+#include <flatbuffers/flatbuffers.h>
+#include <tensorflow/lite/interpreter.h>
+#include <tensorflow/lite/kernels/register.h>
+#include <tensorflow/lite/model.h>
+#include <schema_generated.h>
+#include <tensorflow/lite/version.h>
+#include <doctest/doctest.h>
+
+namespace armnnDelegate
+{
+void TileFloat32Test(std::vector<armnn::BackendId>& backends)
+{
+    // Set input data
+    std::vector<float> inputValues =
+    {
+        0.f, 1.f, 2.f,
+        3.f, 4.f, 5.f
+    };
+
+    // Set output data
+    std::vector<float> expectedOutputValues =
+    {
+        0.f, 1.f, 2.f, 0.f, 1.f, 2.f,
+        3.f, 4.f, 5.f, 3.f, 4.f, 5.f,
+
+        0.f, 1.f, 2.f, 0.f, 1.f, 2.f,
+        3.f, 4.f, 5.f, 3.f, 4.f, 5.f
+    };
+
+    // The multiples
+    const std::vector<int32_t> multiplesValues = { 2, 2 };
+
+    // Set shapes
+    const std::vector<int32_t> inputShape = { 2, 3 };
+    const std::vector<int32_t> multiplesShape = { 2 };
+    const std::vector<int32_t> expectedOutputShape = { 4, 6 };
+
+    TileFP32TestImpl(tflite::BuiltinOperator_TILE,
+                     backends,
+                     inputValues,
+                     inputShape,
+                     multiplesValues,
+                     multiplesShape,
+                     expectedOutputValues,
+                     expectedOutputShape);
+}
+
+#if defined(TILE_GPUACC)
+TEST_SUITE("TileTests_GpuAccTests")
+{
+
+    TEST_CASE ("Tile_Float32_GpuAcc_Test")
+    {
+        std::vector<armnn::BackendId> backends = { armnn::Compute::GpuAcc };
+        TileFloat32Test(backends);
+    }
+
+} // TEST_SUITE("Tile_Float32_GpuAcc_Test")
+#endif
+
+TEST_SUITE("TileTests_CpuAccTests")
+{
+
+    TEST_CASE ("Tile_Float32_CpuAcc_Test")
+    {
+        std::vector<armnn::BackendId> backends = { armnn::Compute::CpuAcc };
+        TileFloat32Test(backends);
+    }
+
+} // TEST_SUITE("Tile_Float32_CpuAcc_Test")
+
+TEST_SUITE("TileTests_CpuRefTests")
+{
+
+    TEST_CASE ("Tile_Float32_CpuRef_Test")
+    {
+        std::vector<armnn::BackendId> backends = { armnn::Compute::CpuRef };
+        TileFloat32Test(backends);
+    }
+
+} // TEST_SUITE("Tile_Float32_CpuRef_Test")
+
+} // namespace armnnDelegate
\ No newline at end of file
diff --git a/delegate/test/TileTestHelper.hpp b/delegate/test/TileTestHelper.hpp
new file mode 100644
index 0000000..f376612
--- /dev/null
+++ b/delegate/test/TileTestHelper.hpp
@@ -0,0 +1,149 @@
+//
+// Copyright © 2023 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#pragma once
+
+#include "TestUtils.hpp"
+
+#include <armnn_delegate.hpp>
+#include <DelegateTestInterpreter.hpp>
+
+#include <flatbuffers/flatbuffers.h>
+#include <tensorflow/lite/kernels/register.h>
+#include <tensorflow/lite/version.h>
+
+#include <schema_generated.h>
+
+#include <doctest/doctest.h>
+
+namespace
+{
+std::vector<char> CreateTileTfLiteModel(tflite::BuiltinOperator operatorCode,
+                                        tflite::TensorType inputTensorType,
+                                        const std::vector<int32_t>& inputTensorShape,
+                                        const std::vector<int32_t>& multiplesTensorData,
+                                        const std::vector<int32_t>& multiplesTensorShape,
+                                        const std::vector<int32_t>& outputTensorShape)
+{
+    using namespace tflite;
+    flatbuffers::FlatBufferBuilder flatBufferBuilder;
+
+    std::vector<flatbuffers::Offset<tflite::Buffer>> buffers;
+    buffers.push_back(CreateBuffer(flatBufferBuilder));
+    buffers.push_back(CreateBuffer(flatBufferBuilder));
+    buffers.push_back(CreateBuffer(flatBufferBuilder,
+                                   flatBufferBuilder.CreateVector(
+                                           reinterpret_cast<const uint8_t*>(multiplesTensorData.data()),
+                                           sizeof(int32_t) * multiplesTensorData.size())));
+    buffers.push_back(CreateBuffer(flatBufferBuilder));
+
+    std::array<flatbuffers::Offset<Tensor>, 3> tensors;
+    tensors[0] = CreateTensor(flatBufferBuilder,
+                              flatBufferBuilder.CreateVector<int32_t>(inputTensorShape.data(),
+                                                                      inputTensorShape.size()),
+                              inputTensorType,
+                              1,
+                              flatBufferBuilder.CreateString("input_tensor"));
+
+    tensors[1] = CreateTensor(flatBufferBuilder,
+                              flatBufferBuilder.CreateVector<int32_t>(multiplesTensorShape.data(),
+                                                                      multiplesTensorShape.size()),
+                              TensorType_INT32,
+                              2,
+                              flatBufferBuilder.CreateString("axis_input_tensor"));
+
+    tensors[2] = CreateTensor(flatBufferBuilder,
+                              flatBufferBuilder.CreateVector<int32_t>(outputTensorShape.data(),
+                                                                      outputTensorShape.size()),
+                              inputTensorType,
+                              3,
+                              flatBufferBuilder.CreateString("output_tensor"));
+
+    // Create Operator
+    tflite::BuiltinOptions operatorBuiltinOptionsType = tflite::BuiltinOptions_NONE;
+    flatbuffers::Offset<void> operatorBuiltinOption = 0;
+
+    const std::vector<int> operatorInputs {0, 1};
+    const std::vector<int> operatorOutputs {2};
+
+    flatbuffers::Offset<Operator> tileOperator =
+            CreateOperator(flatBufferBuilder,
+                           0,
+                           flatBufferBuilder.CreateVector<int32_t>(operatorInputs.data(), operatorInputs.size()),
+                           flatBufferBuilder.CreateVector<int32_t>(operatorOutputs.data(), operatorOutputs.size()),
+                           operatorBuiltinOptionsType,
+                           operatorBuiltinOption);
+
+    const std::vector<int> subgraphInputs{0, 1};
+    const std::vector<int> subgraphOutputs{2};
+    flatbuffers::Offset <SubGraph> subgraph =
+            CreateSubGraph(flatBufferBuilder,
+                           flatBufferBuilder.CreateVector(tensors.data(), tensors.size()),
+                           flatBufferBuilder.CreateVector<int32_t>(subgraphInputs.data(), subgraphInputs.size()),
+                           flatBufferBuilder.CreateVector<int32_t>(subgraphOutputs.data(), subgraphOutputs.size()),
+                           flatBufferBuilder.CreateVector(&tileOperator, 1));
+
+    flatbuffers::Offset <flatbuffers::String> modelDescription =
+            flatBufferBuilder.CreateString("ArmnnDelegate: Tile Operator Model");
+    flatbuffers::Offset <OperatorCode> opCode = CreateOperatorCode(flatBufferBuilder, operatorCode);
+
+    flatbuffers::Offset <Model> flatbufferModel =
+            CreateModel(flatBufferBuilder,
+                        TFLITE_SCHEMA_VERSION,
+                        flatBufferBuilder.CreateVector(&opCode, 1),
+                        flatBufferBuilder.CreateVector(&subgraph, 1),
+                        modelDescription,
+                        flatBufferBuilder.CreateVector(buffers.data(), buffers.size()));
+
+    flatBufferBuilder.Finish(flatbufferModel, armnnDelegate::FILE_IDENTIFIER);
+
+    return std::vector<char>(flatBufferBuilder.GetBufferPointer(),
+                             flatBufferBuilder.GetBufferPointer() + flatBufferBuilder.GetSize());
+}
+
+void TileFP32TestImpl(tflite::BuiltinOperator operatorCode,
+                      std::vector<armnn::BackendId>& backends,
+                      std::vector<float>& inputValues,
+                      std::vector<int32_t> inputShape,
+                      std::vector<int32_t> multiplesValues,
+                      std::vector<int32_t> multiplesShapes,
+                      std::vector<float>& expectedOutputValues,
+                      std::vector<int32_t> expectedOutputShape)
+{
+    using namespace delegateTestInterpreter;
+
+    std::vector<char> modelBuffer = CreateTileTfLiteModel(operatorCode,
+                                                          ::tflite::TensorType::TensorType_FLOAT32,
+                                                          inputShape,
+                                                          multiplesValues,
+                                                          multiplesShapes,
+                                                          expectedOutputShape);
+
+    // Setup interpreter with just TFLite Runtime.
+    auto tfLiteInterpreter = DelegateTestInterpreter(modelBuffer);
+    CHECK(tfLiteInterpreter.AllocateTensors() == kTfLiteOk);
+    CHECK(tfLiteInterpreter.FillInputTensor<float>(inputValues, 0) == kTfLiteOk);
+    CHECK(tfLiteInterpreter.FillInputTensor<int32_t>(multiplesValues, 1) == kTfLiteOk);
+    CHECK(tfLiteInterpreter.Invoke() == kTfLiteOk);
+    std::vector<float>   tfLiteOutputValues = tfLiteInterpreter.GetOutputResult<float>(0);
+    std::vector<int32_t> tfLiteOutputShape  = tfLiteInterpreter.GetOutputShape(0);
+
+    // Setup interpreter with Arm NN Delegate applied.
+    auto armnnInterpreter = DelegateTestInterpreter(modelBuffer, backends);
+    CHECK(armnnInterpreter.AllocateTensors() == kTfLiteOk);
+    CHECK(armnnInterpreter.FillInputTensor<float>(inputValues, 0) == kTfLiteOk);
+    CHECK(armnnInterpreter.FillInputTensor<int32_t>(multiplesValues, 1) == kTfLiteOk);
+    CHECK(armnnInterpreter.Invoke() == kTfLiteOk);
+    std::vector<float>   armnnOutputValues = armnnInterpreter.GetOutputResult<float>(0);
+    std::vector<int32_t> armnnOutputShape  = armnnInterpreter.GetOutputShape(0);
+
+    armnnDelegate::CompareOutputData<float>(tfLiteOutputValues, armnnOutputValues, expectedOutputValues);
+    armnnDelegate::CompareOutputShape(tfLiteOutputShape, armnnOutputShape, expectedOutputShape);
+
+    tfLiteInterpreter.Cleanup();
+    armnnInterpreter.Cleanup();
+}
+
+} // anonymous namespace
\ No newline at end of file