GitHub #640 Add support for CEIL operator

* Reference workload
* TfLite Delegate
* TfLite Parser
* Serializer and Deserializer
* Changed fallback tests in delegate to use COS instead of CEIL

Signed-off-by: Teresa Charlin <teresa.charlinreyes@arm.com>
Signed-off-by: Mike Kelly <mike.kelly@arm.com>
Change-Id: I36e0dbff33694182d1dba0c95d463506428e2f04
diff --git a/delegate/classic/src/armnn_delegate.cpp b/delegate/classic/src/armnn_delegate.cpp
index b494a36..9b4a7d3 100644
--- a/delegate/classic/src/armnn_delegate.cpp
+++ b/delegate/classic/src/armnn_delegate.cpp
@@ -602,6 +602,12 @@
                                      tfLiteNode,
                                      nodeIndex,
                                      kTfLiteBuiltinCast);
+        case kTfLiteBuiltinCeil:
+            return VisitElementwiseUnaryOperator(delegateData,
+                                                 tfLiteContext,
+                                                 tfLiteNode,
+                                                 nodeIndex,
+                                                 armnn::UnaryOperation::Ceil);
         case kTfLiteBuiltinConcatenation:
             return VisitControlOperator(delegateData,
                                         tfLiteContext,
diff --git a/delegate/test/DelegateOptionsTest.cpp b/delegate/test/DelegateOptionsTest.cpp
index fd1ef88..349e5d0 100644
--- a/delegate/test/DelegateOptionsTest.cpp
+++ b/delegate/test/DelegateOptionsTest.cpp
@@ -177,7 +177,7 @@
     std::vector<armnn::BackendId> backends = { armnn::Compute::CpuRef };
     std::vector<int32_t> tensorShape { 1, 2, 2, 1 };
     std::vector<float> inputData = { 0.1f, -2.1f, 3.0f, -4.6f };
-    std::vector<float> expectedResult = { 1.0f, -2.0f, 3.0f, -4.0f };
+    std::vector<float> expectedResult = { 0.995004177f, -0.504846036f, -0.989992499f, -0.112152621f };
 
     // Create options_keys and options_values char array
     size_t num_options = keys.size();
diff --git a/delegate/test/DelegateOptionsTestHelper.hpp b/delegate/test/DelegateOptionsTestHelper.hpp
index b6974c9..148a6d2 100644
--- a/delegate/test/DelegateOptionsTestHelper.hpp
+++ b/delegate/test/DelegateOptionsTestHelper.hpp
@@ -152,10 +152,10 @@
                              flatBufferBuilder.GetBufferPointer() + flatBufferBuilder.GetSize());
 }
 
-std::vector<char> CreateCeilTfLiteModel(tflite::TensorType tensorType,
-                                        const std::vector <int32_t>& tensorShape,
-                                        float quantScale = 1.0f,
-                                        int quantOffset = 0)
+std::vector<char> CreateCosTfLiteModel(tflite::TensorType tensorType,
+                                       const std::vector <int32_t>& tensorShape,
+                                       float quantScale = 1.0f,
+                                       int quantOffset = 0)
 {
     using namespace tflite;
     flatbuffers::FlatBufferBuilder flatBufferBuilder;
@@ -199,7 +199,7 @@
     flatbuffers::Offset<flatbuffers::String> modelDescription =
         flatBufferBuilder.CreateString("ArmnnDelegate: CEIL Operator Model");
     flatbuffers::Offset<OperatorCode> operatorCode =
-        CreateOperatorCode(flatBufferBuilder, tflite::BuiltinOperator_CEIL);
+        CreateOperatorCode(flatBufferBuilder, tflite::BuiltinOperator_COS);
 
     const std::vector<int32_t> subgraphInputs({0});
     const std::vector<int32_t> subgraphOutputs({1});
@@ -277,10 +277,10 @@
                                   int quantOffset  = 0)
 {
     using namespace delegateTestInterpreter;
-    std::vector<char> modelBuffer = CreateCeilTfLiteModel(tensorType,
-                                                          tensorShape,
-                                                          quantScale,
-                                                          quantOffset);
+    std::vector<char> modelBuffer = CreateCosTfLiteModel(tensorType,
+                                                         tensorShape,
+                                                         quantScale,
+                                                         quantOffset);
 
     // Setup interpreter with just TFLite Runtime.
     auto tfLiteInterpreter = DelegateTestInterpreter(modelBuffer);
diff --git a/delegate/test/ElementwiseUnaryTest.cpp b/delegate/test/ElementwiseUnaryTest.cpp
index 6331436..b4bdd29 100644
--- a/delegate/test/ElementwiseUnaryTest.cpp
+++ b/delegate/test/ElementwiseUnaryTest.cpp
@@ -295,6 +295,26 @@
     ElementwiseUnaryFP32Test(tflite::BuiltinOperator_ABS, backends, inputValues, expectedOutputValues);
 }
 
+TEST_CASE ("Ceil_Float32_CpuRef_Test")
+{
+    // Create the ArmNN Delegate
+    std::vector<armnn::BackendId> backends = { armnn::Compute::CpuRef };
+    // Set input data
+    std::vector<float> inputValues
+    {
+        0.0f, 1.1f, -16.1f,
+        0.5f, -0.5f, -1.3f
+    };
+    // Set output data
+    std::vector<float> expectedOutputValues
+    {
+        0.0f, 2.0f, -16.0f,
+        1.0f, 0.0f, -1.0f
+    };
+
+    ElementwiseUnaryFP32Test(tflite::BuiltinOperator_CEIL, backends, inputValues, expectedOutputValues);
+}
+
 TEST_CASE ("Exp_Float32_CpuRef_Test")
 {
     // Create the ArmNN Delegate
diff --git a/include/armnn/Types.hpp b/include/armnn/Types.hpp
index 3c64e82..513ea3f 100644
--- a/include/armnn/Types.hpp
+++ b/include/armnn/Types.hpp
@@ -130,7 +130,8 @@
     Neg        = 4,
     LogicalNot = 5,
     Log        = 6,
-    Sin        = 7
+    Sin        = 7,
+    Ceil       = 8
 };
 
 enum class BinaryOperation
diff --git a/src/armnnDeserializer/Deserializer.cpp b/src/armnnDeserializer/Deserializer.cpp
index ed92188..3bd24bd 100644
--- a/src/armnnDeserializer/Deserializer.cpp
+++ b/src/armnnDeserializer/Deserializer.cpp
@@ -592,6 +592,8 @@
     {
         case armnnSerializer::UnaryOperation::UnaryOperation_Abs:
             return armnn::UnaryOperation::Abs;
+        case armnnSerializer::UnaryOperation::UnaryOperation_Ceil:
+            return armnn::UnaryOperation::Ceil;
         case armnnSerializer::UnaryOperation::UnaryOperation_Rsqrt:
             return armnn::UnaryOperation::Rsqrt;
         case armnnSerializer::UnaryOperation::UnaryOperation_Sqrt:
diff --git a/src/armnnDeserializer/test/DeserializeElementwiseUnary.cpp b/src/armnnDeserializer/test/DeserializeElementwiseUnary.cpp
index 0a89f48..b62fb07 100644
--- a/src/armnnDeserializer/test/DeserializeElementwiseUnary.cpp
+++ b/src/armnnDeserializer/test/DeserializeElementwiseUnary.cpp
@@ -1,5 +1,5 @@
 //
-// Copyright © 2021 Arm Ltd and Contributors. All rights reserved.
+// Copyright © 2021,2023 Arm Ltd and Contributors. All rights reserved.
 // SPDX-License-Identifier: MIT
 //
 
@@ -170,4 +170,21 @@
         {{"OutputLayer", {0.50636564111f, -0.23237376165f, -0.76249375473f, -0.4794255386f,
                                                 0.0f, 0.99988301347f, 0.35905835402f, -0.50636564111f}}});
 }
+
+struct SimpleCeilFixture : ElementwiseUnaryFixture
+{
+    SimpleCeilFixture() : ElementwiseUnaryFixture("[ 1, 2, 2, 2 ]", // inputShape
+                                                  "[ 1, 2, 2, 2 ]", // outputShape
+                                                  "Float32",        // dataType
+                                                  "Ceil")           // unaryOperation
+    {}
+};
+
+FIXTURE_TEST_CASE(SimpleCeilTest, SimpleCeilFixture)
+{
+    RunTest<4, armnn::DataType::Float32>(
+            0,
+            {{"InputLayer", {-100.0f, -50.5f, -25.9999f, -0.5f, 0.0f, 1.5555f, 25.5f, 100.0f}}},
+            {{"OutputLayer", {-100.0f, -50.0f, -25.0f, 0.0f, 0.0f, 2.0f, 26.0f, 100.0f}}});
+}
 }
\ No newline at end of file
diff --git a/src/armnnSerializer/ArmnnSchema.fbs b/src/armnnSerializer/ArmnnSchema.fbs
index fb3bc01..a8b5d72 100644
--- a/src/armnnSerializer/ArmnnSchema.fbs
+++ b/src/armnnSerializer/ArmnnSchema.fbs
@@ -351,7 +351,8 @@
     Neg = 4,
     LogicalNot = 5,
     Log = 6,
-    Sin = 7
+    Sin = 7,
+    Ceil = 8
 }
 
 table ElementwiseUnaryDescriptor {
diff --git a/src/armnnSerializer/SerializerUtils.cpp b/src/armnnSerializer/SerializerUtils.cpp
index 703f56f..2188fdc 100644
--- a/src/armnnSerializer/SerializerUtils.cpp
+++ b/src/armnnSerializer/SerializerUtils.cpp
@@ -134,6 +134,8 @@
     {
         case armnn::UnaryOperation::Abs:
             return armnnSerializer::UnaryOperation::UnaryOperation_Abs;
+        case armnn::UnaryOperation::Ceil:
+            return armnnSerializer::UnaryOperation::UnaryOperation_Ceil;
         case armnn::UnaryOperation::Rsqrt:
             return armnnSerializer::UnaryOperation::UnaryOperation_Rsqrt;
         case armnn::UnaryOperation::Sqrt:
diff --git a/src/armnnSerializer/test/SerializerTests.cpp b/src/armnnSerializer/test/SerializerTests.cpp
index 90d7789..6b9b5df 100644
--- a/src/armnnSerializer/test/SerializerTests.cpp
+++ b/src/armnnSerializer/test/SerializerTests.cpp
@@ -1056,7 +1056,7 @@
 TEST_CASE("SerializeElementwiseUnary")
 {
     using op = armnn::UnaryOperation;
-    std::initializer_list<op> allUnaryOperations = {op::Abs, op::Exp, op::Sqrt, op::Rsqrt, op::Neg,
+    std::initializer_list<op> allUnaryOperations = {op::Abs, op::Ceil, op::Exp, op::Sqrt, op::Rsqrt, op::Neg,
                                                     op::LogicalNot, op::Log, op::Sin};
 
     for (auto unaryOperation : allUnaryOperations)
diff --git a/src/armnnTfLiteParser/TfLiteParser.cpp b/src/armnnTfLiteParser/TfLiteParser.cpp
index ee4cadd..2a7f049 100644
--- a/src/armnnTfLiteParser/TfLiteParser.cpp
+++ b/src/armnnTfLiteParser/TfLiteParser.cpp
@@ -729,6 +729,7 @@
     m_ParserFunctions[tflite::BuiltinOperator_AVERAGE_POOL_2D]         = &TfLiteParserImpl::ParseAveragePool2D;
     m_ParserFunctions[tflite::BuiltinOperator_BATCH_TO_SPACE_ND]       = &TfLiteParserImpl::ParseBatchToSpaceND;
     m_ParserFunctions[tflite::BuiltinOperator_BATCH_MATMUL]            = &TfLiteParserImpl::ParseBatchMatMul;
+    m_ParserFunctions[tflite::BuiltinOperator_CEIL]                    = &TfLiteParserImpl::ParseCeil;
     m_ParserFunctions[tflite::BuiltinOperator_CAST]                    = &TfLiteParserImpl::ParseCast;
     m_ParserFunctions[tflite::BuiltinOperator_CONCATENATION]           = &TfLiteParserImpl::ParseConcatenation;
     m_ParserFunctions[tflite::BuiltinOperator_CONV_2D]                 = &TfLiteParserImpl::ParseConv2D;
@@ -4509,6 +4510,11 @@
     ParseElementwiseUnary(subgraphIndex, operatorIndex, armnn::UnaryOperation::Abs);
 }
 
+void TfLiteParserImpl::ParseCeil(size_t subgraphIndex, size_t operatorIndex)
+{
+    ParseElementwiseUnary(subgraphIndex, operatorIndex, armnn::UnaryOperation::Ceil);
+}
+
 void TfLiteParserImpl::ParseExp(size_t subgraphIndex, size_t operatorIndex)
 {
     ParseElementwiseUnary(subgraphIndex, operatorIndex, armnn::UnaryOperation::Exp);
diff --git a/src/armnnTfLiteParser/TfLiteParser.hpp b/src/armnnTfLiteParser/TfLiteParser.hpp
index ec03c23..91fad43 100644
--- a/src/armnnTfLiteParser/TfLiteParser.hpp
+++ b/src/armnnTfLiteParser/TfLiteParser.hpp
@@ -118,6 +118,7 @@
     void ParseBatchMatMul(size_t subgraphIndex, size_t operatorIndex);
     void ParseBatchToSpaceND(size_t subgraphIndex, size_t operatorIndex);
     void ParseCast(size_t subgraphIndex, size_t operatorIndex);
+    void ParseCeil(size_t subgraphIndex, size_t operatorIndex);
     void ParseComparison(size_t subgraphIndex, size_t operatorIndex, armnn::ComparisonOperation comparisonOperation);
     void ParseConcatenation(size_t subgraphIndex, size_t operatorIndex);
     void ParseConv2D(size_t subgraphIndex, size_t operatorIndex);
diff --git a/src/armnnTfLiteParser/test/ElementWiseUnary.cpp b/src/armnnTfLiteParser/test/ElementWiseUnary.cpp
index 67c2080..ffdf1cc 100644
--- a/src/armnnTfLiteParser/test/ElementWiseUnary.cpp
+++ b/src/armnnTfLiteParser/test/ElementWiseUnary.cpp
@@ -1,5 +1,5 @@
 //
-// Copyright © 2022 Arm Ltd and Contributors. All rights reserved.
+// Copyright © 2021-2023 Arm Ltd and Contributors. All rights reserved.
 // SPDX-License-Identifier: MIT
 //
 
@@ -180,4 +180,15 @@
                                                              0.4794255386f, -0.99177885344f, -0.8414709848f} }});
 }
 
+struct SimpleCeilFixture : public ElementWiseUnaryFixture
+{
+    SimpleCeilFixture() : ElementWiseUnaryFixture("CEIL", "FLOAT32", "[ 1, 2, 3, 1 ]", "[ 1, 2, 3, 1 ]") {}
+};
+
+TEST_CASE_FIXTURE(SimpleCeilFixture, "ParseCeil")
+{
+    RunTest<4, armnn::DataType::Float32>(0, {{ "inputTensor", { -50.5f, -25.9999f, -0.5f, 0.0f, 1.5555f, 25.5f } }},
+                                            {{ "outputTensor",{ -50.0f, -25.0f, 0.0f, 0.0f, 2.0f, 26.0f} }});
+}
+
 }
diff --git a/src/backends/reference/workloads/Ceil.hpp b/src/backends/reference/workloads/Ceil.hpp
new file mode 100644
index 0000000..2e415b9
--- /dev/null
+++ b/src/backends/reference/workloads/Ceil.hpp
@@ -0,0 +1,25 @@
+//
+// Copyright © 2023 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#pragma once
+
+#include <iostream>
+
+namespace armnn
+{
+template<typename T>
+struct ceil
+    {
+        typedef T result_type;
+        typedef T argument_type;
+
+        T
+        operator () (const T&  inputData) const
+        {
+            return std::ceil(inputData);
+        }
+    };
+
+} //namespace armnn
diff --git a/src/backends/reference/workloads/ElementwiseFunction.cpp b/src/backends/reference/workloads/ElementwiseFunction.cpp
index 82bcf99..c5b0ad1 100644
--- a/src/backends/reference/workloads/ElementwiseFunction.cpp
+++ b/src/backends/reference/workloads/ElementwiseFunction.cpp
@@ -1,14 +1,14 @@
 //
-// Copyright © 2017 Arm Ltd and Contributors. All rights reserved.
+// Copyright © 2017-2021,2023 Arm Ltd and Contributors. All rights reserved.
 // SPDX-License-Identifier: MIT
 //
 
 #include "ElementwiseFunction.hpp"
 #include "Broadcast.hpp"
-#include <functional>
 #include "Minimum.hpp"
 #include "Maximum.hpp"
 #include "Abs.hpp"
+#include "Ceil.hpp"
 #include "Exp.hpp"
 #include "Log.hpp"
 #include "Rsqrt.hpp"
@@ -85,6 +85,7 @@
 
 // Unary
 template struct armnn::ElementwiseUnaryFunction<armnn::abs<float>>;
+template struct armnn::ElementwiseUnaryFunction<armnn::ceil<float>>;
 template struct armnn::ElementwiseUnaryFunction<armnn::exp<float>>;
 template struct armnn::ElementwiseUnaryFunction<armnn::log<float>>;
 template struct armnn::ElementwiseUnaryFunction<std::negate<float>>;
diff --git a/src/backends/reference/workloads/RefElementwiseUnaryWorkload.cpp b/src/backends/reference/workloads/RefElementwiseUnaryWorkload.cpp
index 4bd5a51..f4775e0 100644
--- a/src/backends/reference/workloads/RefElementwiseUnaryWorkload.cpp
+++ b/src/backends/reference/workloads/RefElementwiseUnaryWorkload.cpp
@@ -1,5 +1,5 @@
 //
-// Copyright © 2022 Arm Ltd and Contributors. All rights reserved.
+// Copyright © 2020-2023 Arm Ltd and Contributors. All rights reserved.
 // SPDX-License-Identifier: MIT
 //
 
@@ -10,6 +10,7 @@
 #include "Encoders.hpp"
 #include "RefWorkloadUtils.hpp"
 #include "Abs.hpp"
+#include "Ceil.hpp"
 #include "Exp.hpp"
 #include "Log.hpp"
 #include "Rsqrt.hpp"
@@ -56,6 +57,7 @@
     std::unique_ptr<Encoder<OutType>> output= MakeEncoder<OutType>(outputInfo, outputs[0]->Map());
 
     using AbsFunction   = ElementwiseUnaryFunction<abs<InType>>;
+    using CeilFunction  = ElementwiseUnaryFunction<ceil<InType>>;
     using ExpFunction   = ElementwiseUnaryFunction<exp<InType>>;
     using LogFunction   = ElementwiseUnaryFunction<log<InType>>;
     using NegFunction   = ElementwiseUnaryFunction<std::negate<InType>>;
@@ -70,6 +72,11 @@
             AbsFunction(inShape, outShape, *input, *output);
             break;
         }
+        case UnaryOperation::Ceil:
+        {
+            CeilFunction(inShape, outShape, *input, *output);
+            break;
+        }
         case UnaryOperation::Exp:
         {
             ExpFunction(inShape, outShape, *input, *output);