COMPMID-2248
L2NormalizeLayer: negative axis

Change-Id: Ic164d7a9ddf1615a2e3b0e10430c34194a70f221
Signed-off-by: Manuel Bottini <manuel.bottini@arm.com>
Reviewed-on: https://review.mlplatform.org/c/1127
Tested-by: Arm Jenkins <bsgcomp@arm.com>
Reviewed-by: Michalis Spyrou <michalis.spyrou@arm.com>
Comments-Addressed: Arm Jenkins <bsgcomp@arm.com>
diff --git a/tests/validation/CL/L2NormalizeLayer.cpp b/tests/validation/CL/L2NormalizeLayer.cpp
index fdbfa3e..beedd81 100644
--- a/tests/validation/CL/L2NormalizeLayer.cpp
+++ b/tests/validation/CL/L2NormalizeLayer.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017-2018 ARM Limited.
+ * Copyright (c) 2017-2019 ARM Limited.
  *
  * SPDX-License-Identifier: MIT
  *
@@ -46,8 +46,8 @@
 constexpr AbsoluteTolerance<float> tolerance_f32(0.00001f);
 constexpr AbsoluteTolerance<float> tolerance_f16(0.2f);
 
-auto data = concat(combine(framework::dataset::make("DataLayout", { DataLayout::NCHW }), framework::dataset::make("Axis", { 0, 1, 2 })), combine(framework::dataset::make("DataLayout", { DataLayout::NHWC }),
-                   framework::dataset::make("Axis", { 1, 2 })));
+auto data = concat(combine(framework::dataset::make("DataLayout", { DataLayout::NCHW }), framework::dataset::make("Axis", { -1, 0, 2 })), combine(framework::dataset::make("DataLayout", { DataLayout::NHWC }),
+                   framework::dataset::make("Axis", { -2, 2 })));
 
 } // namespace
 
@@ -61,8 +61,9 @@
                                              TensorInfo(TensorShape(128U, 64U), 1, DataType::F32), // Mismatching shape input/output
                                              TensorInfo(TensorShape(128U, 64U), 2, DataType::F32), // Number of Input channels != 1
                                              TensorInfo(TensorShape(128U, 64U), 1, DataType::S16), // DataType != F32
-                                             TensorInfo(TensorShape(128U, 64U), 1, DataType::F32), // Axis >= num_max_dimensions
-                                             TensorInfo(TensorShape(128U, 64U), 1, DataType::F32), // Axis > 2
+                                             TensorInfo(TensorShape(128U, 64U), 1, DataType::F32),
+                                             TensorInfo(TensorShape(128U, 64U), 1, DataType::F32),
+                                             TensorInfo(TensorShape(128U, 64U), 1, DataType::F32),
                                              TensorInfo(TensorShape(128U, 64U), 1, DataType::F32)
                                            }),
     framework::dataset::make("OutputInfo", { TensorInfo(TensorShape(128U, 64U), 1, DataType::F16),
@@ -71,10 +72,19 @@
                                              TensorInfo(TensorShape(128U, 64U), 1, DataType::S16),
                                              TensorInfo(TensorShape(128U, 64U), 1, DataType::F32),
                                              TensorInfo(TensorShape(128U, 64U), 1, DataType::F32),
+                                             TensorInfo(TensorShape(128U, 64U), 1, DataType::F32),
                                              TensorInfo(TensorShape(128U, 64U), 1, DataType::F32)
                                            })),
-    framework::dataset::make("Axis",       { 0U, 0U, 0U, 0U, static_cast<unsigned int>(TensorShape::num_max_dimensions), 3U, 0U })),
-    framework::dataset::make("Expected",   { false, false, false, false, false, false, true })),
+    framework::dataset::make("Axis",       {
+                                            0,
+                                            0,
+                                            0,
+                                            0,
+                                            static_cast<int>(TensorShape::num_max_dimensions),
+                                            3,
+                                            -2,
+                                            0 })),
+    framework::dataset::make("Expected",   { false, false, false, false, true, true, true, true })),
     input_info, output_info, axis, expected)
 {
     bool is_valid = bool(CLL2NormalizeLayer::validate(&input_info.clone()->set_is_resizable(false),
diff --git a/tests/validation/NEON/L2NormalizeLayer.cpp b/tests/validation/NEON/L2NormalizeLayer.cpp
index 3164a65..17147c1 100644
--- a/tests/validation/NEON/L2NormalizeLayer.cpp
+++ b/tests/validation/NEON/L2NormalizeLayer.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017-2018 ARM Limited.
+ * Copyright (c) 2017-2019 ARM Limited.
  *
  * SPDX-License-Identifier: MIT
  *
@@ -59,8 +59,9 @@
                                              TensorInfo(TensorShape(128U, 64U), 1, DataType::F32), // Mismatching shape input/output
                                              TensorInfo(TensorShape(128U, 64U), 2, DataType::F32), // Number of Input channels != 1
                                              TensorInfo(TensorShape(128U, 64U), 1, DataType::S16), // DataType != F32
-                                             TensorInfo(TensorShape(128U, 64U), 1, DataType::F32), // Axis >= num_max_dimensions
-                                             TensorInfo(TensorShape(128U, 64U), 1, DataType::F32), // Axis > 2
+                                             TensorInfo(TensorShape(128U, 64U), 1, DataType::F32),
+                                             TensorInfo(TensorShape(128U, 64U), 1, DataType::F32),
+                                             TensorInfo(TensorShape(128U, 64U), 1, DataType::F32),
                                              TensorInfo(TensorShape(128U, 64U), 1, DataType::F32)
                                            }),
     framework::dataset::make("OutputInfo", { TensorInfo(TensorShape(128U, 64U), 1, DataType::F16),
@@ -69,10 +70,19 @@
                                              TensorInfo(TensorShape(128U, 64U), 1, DataType::S16),
                                              TensorInfo(TensorShape(128U, 64U), 1, DataType::F32),
                                              TensorInfo(TensorShape(128U, 64U), 1, DataType::F32),
+                                             TensorInfo(TensorShape(128U, 64U), 1, DataType::F32),
                                              TensorInfo(TensorShape(128U, 64U), 1, DataType::F32)
                                            })),
-    framework::dataset::make("Axis",       { 0U, 0U, 0U, 0U, static_cast<unsigned int>(TensorShape::num_max_dimensions), 3U, 0U })),
-    framework::dataset::make("Expected",   { false, false, false, false, false, false, true })),
+    framework::dataset::make("Axis",       {
+                                            0,
+                                            0,
+                                            0,
+                                            0,
+                                            static_cast<int>(TensorShape::num_max_dimensions),
+                                            3,
+                                            -2,
+                                            0 })),
+    framework::dataset::make("Expected",   { false, false, false, false, true, true, true, true })),
     input_info, output_info, axis, expected)
 {
     bool is_valid = bool(NEL2NormalizeLayer::validate(&input_info.clone()->set_is_resizable(false),
@@ -89,7 +99,7 @@
 TEST_SUITE(FP32)
 FIXTURE_DATA_TEST_CASE(RunSmall, NEL2NormalizeLayerFixture<float>, framework::DatasetMode::PRECOMMIT,
                        combine(combine(combine(combine(datasets::SmallShapes(), framework::dataset::make("DataType", DataType::F32)), framework::dataset::make("DataLayout", { DataLayout::NCHW, DataLayout::NHWC })),
-                                       framework::dataset::make("Axis", { 0, 1, 2 })),
+                                       framework::dataset::make("Axis", { -1, 0, 2 })),
                                framework::dataset::make("Epsilon", { 1e-12 })))
 {
     // Validate output
@@ -98,7 +108,7 @@
 
 FIXTURE_DATA_TEST_CASE(RunLarge, NEL2NormalizeLayerFixture<float>, framework::DatasetMode::NIGHTLY,
                        combine(combine(combine(combine(datasets::LargeShapes(), framework::dataset::make("DataType", DataType::F32)), framework::dataset::make("DataLayout", { DataLayout::NCHW, DataLayout::NHWC })),
-                                       framework::dataset::make("Axis", { 0, 1, 2 })),
+                                       framework::dataset::make("Axis", { -1, 0, 2 })),
                                framework::dataset::make("Epsilon", { 1e-12 })))
 {
     // Validate output
@@ -110,7 +120,7 @@
 TEST_SUITE(FP16)
 FIXTURE_DATA_TEST_CASE(RunSmall, NEL2NormalizeLayerFixture<half>, framework::DatasetMode::PRECOMMIT,
                        combine(combine(combine(combine(datasets::SmallShapes(), framework::dataset::make("DataType", DataType::F16)), framework::dataset::make("DataLayout", { DataLayout::NCHW, DataLayout::NHWC })),
-                                       framework::dataset::make("Axis", { 0, 1, 2 })),
+                                       framework::dataset::make("Axis", { -1, 0, 2 })),
                                framework::dataset::make("Epsilon", { 1e-12 })))
 {
     // Validate output
@@ -119,7 +129,7 @@
 
 FIXTURE_DATA_TEST_CASE(RunLarge, NEL2NormalizeLayerFixture<half>, framework::DatasetMode::NIGHTLY,
                        combine(combine(combine(combine(datasets::LargeShapes(), framework::dataset::make("DataType", DataType::F16)), framework::dataset::make("DataLayout", { DataLayout::NCHW, DataLayout::NHWC })),
-                                       framework::dataset::make("Axis", { 0, 1, 2 })),
+                                       framework::dataset::make("Axis", { -1, 0, 2 })),
                                framework::dataset::make("Epsilon", { 1e-12 })))
 {
     // Validate output
diff --git a/tests/validation/fixtures/L2NormalizeLayerFixture.h b/tests/validation/fixtures/L2NormalizeLayerFixture.h
index 574722b..e3e1510 100644
--- a/tests/validation/fixtures/L2NormalizeLayerFixture.h
+++ b/tests/validation/fixtures/L2NormalizeLayerFixture.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017-2018 ARM Limited.
+ * Copyright (c) 2017-2019 ARM Limited.
  *
  * SPDX-License-Identifier: MIT
  *
@@ -40,12 +40,16 @@
 {
 namespace validation
 {
+namespace
+{
+constexpr int max_input_tensor_dim = 3;
+} // namespace
 template <typename TensorType, typename AccessorType, typename FunctionType, typename T>
 class L2NormalizeLayerValidationFixture : public framework::Fixture
 {
 public:
     template <typename...>
-    void setup(TensorShape shape, DataType data_type, DataLayout data_layout, unsigned int axis, float epsilon)
+    void setup(TensorShape shape, DataType data_type, DataLayout data_layout, int axis, float epsilon)
     {
         _target    = compute_target(shape, data_type, data_layout, axis, epsilon);
         _reference = compute_reference(shape, data_type, data_layout, axis, epsilon);
@@ -59,7 +63,7 @@
         library->fill(tensor, distribution, 0);
     }
 
-    TensorType compute_target(TensorShape shape, DataType data_type, DataLayout data_layout, unsigned int axis, float epsilon)
+    TensorType compute_target(TensorShape shape, DataType data_type, DataLayout data_layout, int axis, float epsilon)
     {
         if(data_layout == DataLayout::NHWC)
         {
@@ -93,20 +97,21 @@
         return dst;
     }
 
-    SimpleTensor<T> compute_reference(const TensorShape &shape, DataType data_type, DataLayout data_layout, unsigned int axis, float epsilon)
+    SimpleTensor<T> compute_reference(const TensorShape &shape, DataType data_type, DataLayout data_layout, int axis, float epsilon)
     {
+        uint32_t actual_axis = wrap_around(axis, max_input_tensor_dim);
         if(data_layout == DataLayout::NHWC)
         {
-            switch(axis)
+            switch(actual_axis)
             {
                 case 0:
-                    axis = 2;
+                    actual_axis = 2;
                     break;
                 case 1:
-                    axis = 0;
+                    actual_axis = 0;
                     break;
                 case 2:
-                    axis = 1;
+                    actual_axis = 1;
                     break;
                 default:
                     break;
@@ -118,7 +123,7 @@
         // Fill reference
         fill(src);
 
-        return reference::l2_normalize<T>(src, axis, epsilon);
+        return reference::l2_normalize<T>(src, actual_axis, epsilon);
     }
 
     TensorType      _target{};