MLCE-1248 Removing limitations on zero scale value in quantization.

Currently Arm NN will fail to load models containing quantization
scale value of zero.

Signed-off-by: Colm Donelan <colm.donelan@arm.com>
Change-Id: Ifefcee1279b8667da63d1aa7d42e5d44875f9fbe
diff --git a/shim/sl/canonical/ModelToINetworkTransformer.cpp b/shim/sl/canonical/ModelToINetworkTransformer.cpp
index 8efacaf..4cf2160 100644
--- a/shim/sl/canonical/ModelToINetworkTransformer.cpp
+++ b/shim/sl/canonical/ModelToINetworkTransformer.cpp
@@ -1,5 +1,5 @@
 //
-// Copyright © 2022 Arm Ltd and Contributors. All rights reserved.
+// Copyright © 2022, 2024 Arm Ltd and Contributors. All rights reserved.
 // SPDX-License-Identifier: MIT
 //
 
@@ -31,7 +31,8 @@
     catch (std::exception& e)
     {
         m_ConversionResult = ConversionResult::UnsupportedFeature;
-        VLOG(DRIVER) << "ModelToINetworkTransformer: Unexpected exception: " << e.what();
+        VLOG(DRIVER) << "ModelToINetworkTransformer: Unexpected exception: " << e.what() << " Model was: "
+                     << GetModelSummary(model);
         assert(false);
     }
 }
diff --git a/src/armnn/Network.cpp b/src/armnn/Network.cpp
index 6f33fb6..810abaa 100644
--- a/src/armnn/Network.cpp
+++ b/src/armnn/Network.cpp
@@ -818,16 +818,6 @@
                     throw InvalidArgumentException("Per Axis Quantization is not supported in "
                                                    "Asymmetric Quantization Datatype.");
                 }
-                if ((!info.HasPerAxisQuantization() && info.GetQuantizationScale() == 0.f)
-                    || (info.HasPerAxisQuantization() && (quantizationScales.end() !=
-                    std::find(quantizationScales.begin(), quantizationScales.end(), 0.f)))) {
-                    noErrors = false;
-                    std::stringstream ss;
-                    ss << "output " << i << " of layer " << GetLayerTypeAsCString(layer->GetType())
-                       << " (" << layer->GetNameStr() << ") is of type"
-                       << " Quantized value but the scale parameter has not been set";
-                    ReportError(ss.str(), errMessages);
-                }
                 // Softmax under QuantisedAsymm8 must always be scale (1.0f/256.0f) and offset 0
                 if (!info.HasPerAxisQuantization() && quantizationDataType == DataType::QAsymmU8 &&
                     (info.GetQuantizationScale() != (1.0f / 256.0f) ||
@@ -841,6 +831,7 @@
                     info.SetQuantizationScale((1.0f / 256.0f));
                     info.SetQuantizationOffset(0);
                     outputSlot.SetTensorInfo(info);
+                    ReportError(ss.str(), errMessages);
                 }
                 break;
             default:
diff --git a/src/armnn/Tensor.cpp b/src/armnn/Tensor.cpp
index 3b116d9..f75fc60 100644
--- a/src/armnn/Tensor.cpp
+++ b/src/armnn/Tensor.cpp
@@ -465,8 +465,15 @@
         // NOTE: old default for backward compatibility
         return 1.0f;
     }
-
+    // If this tensor includes multiples scales then you should be calling GetQuantizationScales.
+    // This should be an exception not an assert but unfortunately it breaks many tests.
+    // ToDo: IVGCVSW-8323
     ARMNN_ASSERT(!HasMultipleQuantizationScales());
+//    if (HasMultipleQuantizationScales())
+//    {
+//        throw RuntimeException("Invalid call to GetQuantizationScale on a tensor with multiple scale values. Use "
+//                               "GetQuantizationScales instead.");
+//    }
     return m_Quantization.m_Scales[0];
 }
 
diff --git a/src/armnn/TypesUtils.cpp b/src/armnn/TypesUtils.cpp
index d419ef8..ce9c7fc 100644
--- a/src/armnn/TypesUtils.cpp
+++ b/src/armnn/TypesUtils.cpp
@@ -1,5 +1,5 @@
 //
-// Copyright © 2017 Arm Ltd. All rights reserved.
+// Copyright © 2017, 2024 Arm Ltd. All rights reserved.
 // SPDX-License-Identifier: MIT
 //
 #include <armnn/TypesUtils.hpp>
@@ -32,10 +32,6 @@
     static_assert(IsQuantizedType<QuantizedType>(), "Not an integer type.");
     constexpr QuantizedType max = std::numeric_limits<QuantizedType>::max();
     constexpr QuantizedType min = std::numeric_limits<QuantizedType>::lowest();
-    if (scale == 0.f)
-    {
-        throw armnn::InvalidArgumentException("Quantize: Scale cannot be 0.f");
-    }
     if (std::isnan(value))
     {
         throw armnn::InvalidArgumentException("Quantize: Value is NaN");
@@ -52,10 +48,6 @@
 float armnn::Dequantize(QuantizedType value, float scale, int32_t offset)
 {
     static_assert(IsQuantizedType<QuantizedType>(), "Not an integer type.");
-    if (scale == 0.f)
-    {
-        throw armnn::InvalidArgumentException("Dequantize: Scale cannot be 0.f");
-    }
     if (std::isnan(value))
     {
         throw armnn::InvalidArgumentException("Dequantize: Value is NaN");
diff --git a/src/armnn/test/RuntimeTests.cpp b/src/armnn/test/RuntimeTests.cpp
index 7079f0e..6d5e2ae 100644
--- a/src/armnn/test/RuntimeTests.cpp
+++ b/src/armnn/test/RuntimeTests.cpp
@@ -540,20 +540,12 @@
     std::vector<armnn::BackendId> backends = { armnn::Compute::CpuRef };
     std::vector<std::string>      errMessages;
 
-    try
-    {
-        armnn::IOptimizedNetworkPtr optNet = Optimize(*net,
-                                                      backends,
-                                                      runtime->GetDeviceSpec(),
-                                                      OptimizerOptionsOpaque(),
-                                                      errMessages);
-        FAIL("An exception should have been thrown");
-    }
-    catch (const armnn::InvalidArgumentException&)
-    {
-        // Different exceptions are thrown on different backends
-    }
+    // We expect optimize to work but the errMessages should contain something.
+    CHECK_NOTHROW(armnn::IOptimizedNetworkPtr optNet =
+                      Optimize(*net, backends, runtime->GetDeviceSpec(), OptimizerOptionsOpaque(), errMessages));
     CHECK(errMessages.size() > 0);
+    // Should contain "updated to Scale"
+    CHECK(errMessages[0].find("updated to Scale") != std::string::npos);
 }
 
 TEST_CASE("RuntimeBackendOptions")