IVGCVSW-2607 Implement Input range override mechanism
authorMatteo Martincigh <matteo.martincigh@arm.com>
Thu, 7 Feb 2019 17:51:09 +0000 (17:51 +0000)
committerMatteo Martincigh <matteo.martincigh@arm.com>
Fri, 8 Feb 2019 12:23:05 +0000 (12:23 +0000)
 * Added the OverrideInputRange method to the Quantizer API
 * Created OverrideInputRangeVisitor to implement the override mechanism
 * Moved the quantizer utility functions to the new NetworkQuantizerUtils files
 * Moved the map of quantization ranges out of the StaticRangeVisitor
   and into the NetworkQuantizer
 * Added unit tests
 * Code refactoring and cleanup

Change-Id: I9c1d006c1b6a35fbc04584a832fbe489f8f9276d
Signed-off-by: Matteo Martincigh <matteo.martincigh@arm.com>
13 files changed:
CMakeLists.txt
include/armnn/INetworkQuantizer.hpp
src/armnn/NetworkQuantizer.cpp
src/armnn/NetworkQuantizer.hpp
src/armnn/NetworkQuantizerUtils.cpp [new file with mode: 0644]
src/armnn/NetworkQuantizerUtils.hpp [new file with mode: 0644]
src/armnn/OverrideInputRangeVisitor.cpp [new file with mode: 0644]
src/armnn/OverrideInputRangeVisitor.hpp [new file with mode: 0644]
src/armnn/QuantizerVisitor.cpp
src/armnn/QuantizerVisitor.hpp
src/armnn/StaticRangeVisitor.cpp
src/armnn/StaticRangeVisitor.hpp
src/armnn/test/QuantizerTest.cpp

index 8d43ef8..e8d63b9 100644 (file)
@@ -307,12 +307,16 @@ list(APPEND armnn_sources
     src/armnn/Network.hpp
     src/armnn/NetworkQuantizer.cpp
     src/armnn/NetworkQuantizer.hpp
+    src/armnn/NetworkQuantizerUtils.cpp
+    src/armnn/NetworkQuantizerUtils.hpp
     src/armnn/NetworkUtils.cpp
     src/armnn/NetworkUtils.hpp
     src/armnn/Observable.cpp
     src/armnn/Observable.hpp
     src/armnn/Optimizer.cpp
     src/armnn/Optimizer.hpp
+    src/armnn/OverrideInputRangeVisitor.cpp
+    src/armnn/OverrideInputRangeVisitor.hpp
     src/armnn/Profiling.cpp
     src/armnn/ProfilingEvent.cpp
     src/armnn/ProfilingEvent.hpp
index f3cc13c..5969fa4 100644 (file)
@@ -7,9 +7,6 @@
 
 #include <armnn/INetwork.hpp>
 
-#include <memory>
-
-
 namespace armnn
 {
 
@@ -23,11 +20,14 @@ public:
     static INetworkQuantizerPtr Create(INetwork* inputNetwork);  ///< Create Quantizer object wrapped in unique_ptr
     static void Destroy(INetworkQuantizer* quantizer);           ///< Destroy Quantizer object
 
+    /// Overrides the default quantization values for the input layer with the given id
+    virtual void OverrideInputRange(LayerBindingId layerId, float min, float max) = 0;
+
     /// Extract final quantized network
     virtual INetworkPtr ExportNetwork() = 0;
 
 protected:
-    virtual ~INetworkQuantizer() {};
+    virtual ~INetworkQuantizer() {}
 };
 
 } //namespace armnn
index f8e5ed2..ccbc501 100644 (file)
 #include "Layer.hpp"
 #include "Network.hpp"
 #include "NetworkQuantizer.hpp"
+#include "NetworkQuantizerUtils.hpp"
 
 #include "StaticRangeVisitor.hpp"
 #include "QuantizerVisitor.hpp"
+#include "OverrideInputRangeVisitor.hpp"
 
-#include <map>
 #include <vector>
 #include <cmath>
 
@@ -38,26 +39,29 @@ void INetworkQuantizer::Destroy(INetworkQuantizer *quantizer)
     delete boost::polymorphic_downcast<NetworkQuantizer*>(quantizer);
 }
 
+void NetworkQuantizer::OverrideInputRange(LayerBindingId layerId, float min, float max)
+{
+    const Graph& graph = boost::polymorphic_downcast<const Network*>(m_InputNetwork)->GetGraph();
+    auto inputLayers = graph.GetInputLayers();
+
+    // Walk the input layers of the graph and override the quantization parameters of the one with the given id
+    OverrideInputRangeVisitor overrideInputRangeVisitor(m_GuidToRangesMap, layerId, MinMaxRange{min, max});
+    VisitLayers(inputLayers, overrideInputRangeVisitor);
+}
+
 INetworkPtr NetworkQuantizer::ExportNetwork()
 {
     const Graph& graph = boost::polymorphic_downcast<const Network*>(m_InputNetwork)->GetGraph().TopologicalSort();
-    auto VisitLayers = [&graph](ILayerVisitor& visitor)
-        {
-            for (auto layer : graph)
-            {
-                layer->Accept(visitor);
-            }
-        };
 
     // Step 1) Walk the graph and register min/max values for intermediate tensors
-    StaticRangeVisitor rangeVisitor;
-    VisitLayers(rangeVisitor);
+    StaticRangeVisitor rangeVisitor(m_GuidToRangesMap);
+    VisitLayers(graph, rangeVisitor);
 
     // Step 2) Convert input InputNetwork to Quantized InputNetwork
     QuantizerVisitor quantizerVisitor(&rangeVisitor);
-    VisitLayers(quantizerVisitor);
+    VisitLayers(graph, quantizerVisitor);
 
     return quantizerVisitor.RetrieveFinalNetwork();
 }
 
-} //namespace armn
\ No newline at end of file
+} //namespace armn
index 5543b3a..2f7d365 100644 (file)
@@ -9,6 +9,8 @@
 #include <armnn/INetworkQuantizer.hpp>
 #include <armnn/Types.hpp>
 
+#include <unordered_map>
+
 namespace armnn
 {
 
@@ -17,10 +19,17 @@ class NetworkQuantizer : public INetworkQuantizer
 public:
     NetworkQuantizer(INetwork* inputNetwork) : m_InputNetwork(inputNetwork) {}
 
+    void OverrideInputRange(LayerBindingId layerId, float min, float max) override;
     INetworkPtr ExportNetwork() override;
 
 private:
+    using MinMaxRange  = std::pair<float, float>;
+    using MinMaxRanges = std::vector<MinMaxRange>;
+
     INetwork* m_InputNetwork;
+
+    /// Mapping from Guid to an array of ranges for outputs
+    std::unordered_map<LayerGuid, MinMaxRanges> m_GuidToRangesMap;
 };
 
-} //namespace armnn
\ No newline at end of file
+} //namespace armnn
diff --git a/src/armnn/NetworkQuantizerUtils.cpp b/src/armnn/NetworkQuantizerUtils.cpp
new file mode 100644 (file)
index 0000000..1bec63b
--- /dev/null
@@ -0,0 +1,61 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include "NetworkQuantizerUtils.hpp"
+
+#include <algorithm>
+#include <cmath>
+#include <stdint.h>
+
+namespace armnn
+{
+
+std::pair<int, float> ComputeQAsymmParams(int numBits, double min, double max)
+{
+    BOOST_ASSERT_MSG(min < max, "min >= max will result in invalid quantization.");
+    double highest = (1 << numBits) - 1;
+
+    min = std::min(0.0, min); // min <= 0.0
+    max = std::max(0.0, max); // max >= 0.0
+
+    // Assumes quantization range [0-highest]
+    double scale = (max-min) / highest;
+    double offset = -min / scale;
+
+    // Clamp offset [0-highest]
+    offset = std::max(0.0, std::min(highest, offset));
+
+    return std::make_pair(static_cast<int>(std::round(offset)), static_cast<float>(scale));
+}
+
+ConstTensor CreateQuantizedConst(const ConstTensor& tensor, std::vector<uint8_t>& backing)
+{
+    float scale = 0.0f;
+    int offset = 0;
+
+    // Reserve the backing memory
+    backing.resize(tensor.GetInfo().GetNumElements());
+
+    DataType type = tensor.GetInfo().GetDataType();
+    switch(type)
+    {
+        case DataType::Float32:
+        {
+            Quantize(static_cast<const float*>(tensor.GetMemoryArea()),
+                     backing.data(),
+                     backing.size(),
+                     scale,
+                     offset);
+        }
+            break;
+        default:
+            BOOST_ASSERT_MSG(false, "Can't quantize unsupported data type");
+    }
+
+    TensorInfo qInfo(tensor.GetInfo().GetShape(), DataType::QuantisedAsymm8, scale, offset);
+    return ConstTensor(qInfo, backing);
+}
+
+} // namespace armnn
diff --git a/src/armnn/NetworkQuantizerUtils.hpp b/src/armnn/NetworkQuantizerUtils.hpp
new file mode 100644 (file)
index 0000000..458d21a
--- /dev/null
@@ -0,0 +1,56 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#pragma once
+
+#include <armnn/Tensor.hpp>
+#include <armnn/TypesUtils.hpp>
+#include <armnn/ILayerVisitor.hpp>
+
+#include <utility>
+#include <limits>
+
+#include <boost/assert.hpp>
+
+namespace armnn
+{
+
+std::pair<int, float> ComputeQAsymmParams(int numBits, double min, double max);
+
+template<typename srcType>
+void Quantize(const srcType* src, uint8_t* dst, size_t numElements, float& scale, int& offset)
+{
+    BOOST_ASSERT(src);
+    BOOST_ASSERT(dst);
+
+    float min = std::numeric_limits<srcType>::max();
+    float max = std::numeric_limits<srcType>::lowest();
+    for (size_t i = 0; i < numElements; ++i)
+    {
+        min = std::min(min, src[i]);
+        max = std::max(max, src[i]);
+    }
+
+    auto qParams = ComputeQAsymmParams(8, min, max);
+    offset = qParams.first;
+    scale = qParams.second;
+    for (size_t i = 0; i < numElements; ++i)
+    {
+        dst[i] = armnn::Quantize<uint8_t>(src[i], scale, offset);
+    }
+}
+
+ConstTensor CreateQuantizedConst(const ConstTensor& tensor, std::vector<uint8_t>& backing);
+
+template <typename LayerContainer>
+void VisitLayers(const LayerContainer& layerContainer, ILayerVisitor& visitor)
+{
+    for (auto layer : layerContainer)
+    {
+        layer->Accept(visitor);
+    }
+}
+
+} // namespace armnn
diff --git a/src/armnn/OverrideInputRangeVisitor.cpp b/src/armnn/OverrideInputRangeVisitor.cpp
new file mode 100644 (file)
index 0000000..4c70d3f
--- /dev/null
@@ -0,0 +1,47 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include "OverrideInputRangeVisitor.hpp"
+#include "NetworkQuantizerUtils.hpp"
+#include "Layer.hpp"
+
+#include <boost/assert.hpp>
+
+namespace armnn
+{
+
+OverrideInputRangeVisitor::OverrideInputRangeVisitor(std::unordered_map<LayerGuid, MinMaxRanges>& guidToRangesMap,
+                                                     LayerBindingId layerId,
+                                                     const MinMaxRange& minMaxRange)
+    : m_GuidToRangesMap(guidToRangesMap)
+    , m_LayerId(layerId)
+    , m_MinMaxRange(minMaxRange)
+{}
+
+void OverrideInputRangeVisitor::VisitInputLayer(const IConnectableLayer *layer, LayerBindingId id, const char *name)
+{
+    if (m_LayerId != id)
+    {
+        // Not the layer we are looking for
+        return;
+    }
+
+    SetRange(layer);
+}
+
+void OverrideInputRangeVisitor::SetRange(const IConnectableLayer* layer)
+{
+    BOOST_ASSERT(layer);
+
+    auto& ranges = m_GuidToRangesMap[layer->GetGuid()];
+
+    if (ranges.size() < layer->GetNumOutputSlots())
+    {
+        ranges.resize(layer->GetNumOutputSlots());
+    }
+    ranges[0] = m_MinMaxRange;
+}
+
+} // namespace armnn
diff --git a/src/armnn/OverrideInputRangeVisitor.hpp b/src/armnn/OverrideInputRangeVisitor.hpp
new file mode 100644 (file)
index 0000000..a2da6c7
--- /dev/null
@@ -0,0 +1,45 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#pragma once
+
+#include "NetworkQuantizer.hpp"
+#include "LayerVisitorBase.hpp"
+
+#include <unordered_map>
+
+namespace armnn
+{
+
+/// Visitor object for overriding the input range of the quantized input layers in a network
+class OverrideInputRangeVisitor : public LayerVisitorBase<VisitorNoThrowPolicy>
+{
+private:
+    using MinMaxRange  = std::pair<float, float>;
+    using MinMaxRanges = std::vector<MinMaxRange>;
+
+public:
+    OverrideInputRangeVisitor(std::unordered_map<LayerGuid, MinMaxRanges>& guidToRangesMap,
+                              LayerBindingId layerId,
+                              const MinMaxRange& minMaxRange);
+    ~OverrideInputRangeVisitor() = default;
+
+    void VisitInputLayer(const IConnectableLayer *layer, LayerBindingId id, const char *name = nullptr) override;
+
+private:
+    /// Sets the range for the given input layer
+    void SetRange(const IConnectableLayer* layer);
+
+    /// Mapping from a layer Guid to an array of ranges for outputs
+    std::unordered_map<LayerGuid, MinMaxRanges>& m_GuidToRangesMap;
+
+    /// The id of the input layer of which to override the input range
+    LayerBindingId m_LayerId;
+
+    /// The new input range to be applied to the input layer
+    MinMaxRange m_MinMaxRange;
+};
+
+} // namespace armnn
index 4e07514..1212716 100644 (file)
@@ -6,92 +6,16 @@
 #include "Network.hpp"
 #include "QuantizerVisitor.hpp"
 #include "StaticRangeVisitor.hpp"
-
-#include "armnn/TypesUtils.hpp"
-
-#include <cmath>
-#include <stdint.h>
-#include <limits>
+#include "NetworkQuantizerUtils.hpp"
 
 namespace armnn
 {
 
-namespace {
-
-std::pair<int, float> ComputeQAsymmParams(int numBits, double min, double max)
-{
-    BOOST_ASSERT_MSG(min < max, "Min >= max will result in invalid quantization.");
-    double highest = (1 << numBits)-1;
-
-    min = std::min(0.0, min); // min <= 0.0
-    max = std::max(0.0, max); // max >= 0.0
-
-    // assumes quantization range [0-highest]
-    double scale = (max-min) / highest;
-    double offset = -min / scale;
-
-    // clamp offset [0-highest]
-    offset = std::max(0.0, std::min(highest, offset));
-
-    return std::make_pair(static_cast<int>(std::round(offset)), static_cast<float>(scale));
-}
-
-template<typename srcType>
-void Quantize(const srcType* src, uint8_t* dst, size_t numElements, float &scale, int &offset)
-{
-    BOOST_ASSERT(src);
-    BOOST_ASSERT(dst);
-
-    float min = std::numeric_limits<srcType>::max();
-    float max = std::numeric_limits<srcType>::lowest();
-    for (size_t i = 0; i < numElements; ++i)
-    {
-        min = std::min(min, src[i]);
-        max = std::max(max, src[i]);
-    }
-
-    auto qParams = ComputeQAsymmParams(8, min, max);
-    offset = qParams.first;
-    scale = qParams.second;
-    for (size_t i = 0; i < numElements; ++i)
-    {
-        dst[i] = armnn::Quantize<uint8_t>(src[i], scale, offset);
-    }
-}
-
-ConstTensor CreateQuantizedConst(const ConstTensor& tensor, std::vector<uint8_t> &backing)
-{
-    float scale = 0.0f;
-    int offset = 0;
-    // Reserve the backing memory
-    backing.resize(tensor.GetInfo().GetNumElements());
-
-    DataType type = tensor.GetInfo().GetDataType();
-    switch(type)
-    {
-        case DataType::Float32:
-        {
-            Quantize(static_cast<const float*>( tensor.GetMemoryArea()),
-                     backing.data(),
-                     backing.size(),
-                     scale,
-                     offset);
-        }
-            break;
-        default:
-            BOOST_ASSERT_MSG(false, "Can't quantize unsupported data type");
-    }
-
-    TensorInfo qInfo(tensor.GetInfo().GetShape(), DataType::QuantisedAsymm8, scale, offset);
-    return ConstTensor(qInfo, backing);
-}
-
-} // namespace
-
-QuantizerVisitor::QuantizerVisitor(armnn::StaticRangeVisitor* ranges)
-: m_Ranges(ranges)
-, m_QuantizedNetwork(INetwork::Create())
+QuantizerVisitor::QuantizerVisitor(const StaticRangeVisitor *staticRangeVisitor)
+    : m_StaticRangeVisitor(staticRangeVisitor)
+    , m_QuantizedNetwork(INetwork::Create())
 {
+    BOOST_ASSERT(m_StaticRangeVisitor);
 }
 
 void QuantizerVisitor::SetQuantizedInputConnections(const IConnectableLayer *srcLayer,
@@ -106,17 +30,17 @@ void QuantizerVisitor::SetQuantizedInputConnections(const IConnectableLayer *src
         unsigned int slotIdx = outputSlot->CalculateIndexOnOwner();
         Layer& layerToFind = outputSlot->GetOwningLayer();
 
-        auto found = m_OldToNewGuidMap.find(layerToFind.GetGuid());
-        if (found != m_OldToNewGuidMap.end())
+        auto found = m_OriginalToQuantizedGuidMap.find(layerToFind.GetGuid());
+        if (found != m_OriginalToQuantizedGuidMap.end())
         {
             // Connect the slots in the quantized model
-            IConnectableLayer* prevQuantizedLayer = m_GuidToLayerMap[found->second];
+            IConnectableLayer* prevQuantizedLayer = m_QuantizedGuidToLayerMap[found->second];
             IInputSlot& newInputSlot = quantizedLayer->GetInputSlot(i);
             IOutputSlot& newOutputSlot = prevQuantizedLayer->GetOutputSlot(slotIdx);
             newOutputSlot.Connect(newInputSlot);
 
             // Fetch the min/max ranges that were computed earlier
-            auto range = m_Ranges->GetRange(layerToFind.GetGuid(), i);
+            auto range = m_StaticRangeVisitor->GetRange(layerToFind.GetGuid(), i);
             auto qParams = ComputeQAsymmParams(8, range.first, range.second);
 
             // Set the quantization params
@@ -128,7 +52,7 @@ void QuantizerVisitor::SetQuantizedInputConnections(const IConnectableLayer *src
         }
         else
         {
-            // error in graph traversal order
+            // Error in graph traversal order
             BOOST_ASSERT_MSG(false, "Error in graph traversal");
         }
     }
@@ -136,8 +60,8 @@ void QuantizerVisitor::SetQuantizedInputConnections(const IConnectableLayer *src
 
 void QuantizerVisitor::RecordLayer(const IConnectableLayer* srcLayer, IConnectableLayer* quantizedLayer)
 {
-    m_OldToNewGuidMap[srcLayer->GetGuid()] = quantizedLayer->GetGuid();
-    m_GuidToLayerMap[quantizedLayer->GetGuid()] = quantizedLayer;
+    m_OriginalToQuantizedGuidMap[srcLayer->GetGuid()] = quantizedLayer->GetGuid();
+    m_QuantizedGuidToLayerMap[quantizedLayer->GetGuid()] = quantizedLayer;
 }
 
 void QuantizerVisitor::VisitAdditionLayer(const IConnectableLayer *layer, const char *name)
@@ -200,4 +124,4 @@ void QuantizerVisitor::VisitBatchNormalizationLayer(const IConnectableLayer *lay
     SetQuantizedInputConnections(layer, newLayer);
 }
 
-} //namespace armnn
\ No newline at end of file
+} //namespace armnn
index 0dc4582..dcaccd4 100644 (file)
@@ -6,31 +6,34 @@
 #pragma once
 
 #include "LayerVisitorBase.hpp"
+#include "StaticRangeVisitor.hpp"
+
 #include <armnn/INetwork.hpp>
 #include <armnn/Types.hpp>
+#include <armnn/INetworkQuantizer.hpp>
 
-#include <map>
+#include <unordered_map>
 
 namespace armnn
 {
 
-// Forward declarations
+// Forward declaration
 class StaticRangeVisitor;
 
 /// Visitor object for quantizing layers in a network
 class QuantizerVisitor : public LayerVisitorBase<VisitorNoThrowPolicy>
 {
 public:
-    QuantizerVisitor(StaticRangeVisitor* ranges);
+    QuantizerVisitor(const StaticRangeVisitor* staticRangeVisitor);
     ~QuantizerVisitor() = default;
 
-    // Functions to quantize the individual layers, overridden from ILayerVisitor
-    void VisitInputLayer(const IConnectableLayer *layer, LayerBindingId id, const char *name = nullptr) override;
-    void VisitAdditionLayer(const IConnectableLayer *layer, const char *name = nullptr) override;
-    void VisitActivationLayer(const IConnectableLayer *layer,
+    /// Functions to quantize the individual layers, overridden from ILayerVisitor
+    void VisitInputLayer(const IConnectableLayer* layer, LayerBindingId id, const char* name = nullptr) override;
+    void VisitAdditionLayer(const IConnectableLayer* layer, const char* name = nullptr) override;
+    void VisitActivationLayer(const IConnectableLayerlayer,
                               const ActivationDescriptor& activationDescriptor,
-                              const char *name = nullptr) override;
-    void VisitOutputLayer(const IConnectableLayer *layer, LayerBindingId id, const char *name = nullptr)  override;
+                              const charname = nullptr) override;
+    void VisitOutputLayer(const IConnectableLayer* layer, LayerBindingId id, const char* name = nullptr)  override;
     void VisitBatchNormalizationLayer(const IConnectableLayer* layer,
                                       const BatchNormalizationDescriptor& desc,
                                       const ConstTensor& mean,
@@ -39,22 +42,27 @@ public:
                                       const ConstTensor& gamma,
                                       const char* name = nullptr) override;
 
-    // Extract the quantized network
+    /// Extract the quantized network
     INetworkPtr RetrieveFinalNetwork() { return std::move(m_QuantizedNetwork); }
-private:
 
+private:
     /// Connects the layer to preceeding layers and sets the quantization parameters based on recorded ranges
-    void SetQuantizedInputConnections(const IConnectableLayer *srcLayer, IConnectableLayer *quantizedLayer);
+    void SetQuantizedInputConnections(const IConnectableLayer* srcLayer, IConnectableLayer* quantizedLayer);
 
     /// Record the guids so we can easily find the layers later
     void RecordLayer(const IConnectableLayer* srcLayer, IConnectableLayer* qLayer);
 
+    /// Reference to the static range visitor used to retrieve the quantization ranges
+    const StaticRangeVisitor* m_StaticRangeVisitor;
+
+    /// Quantized version of the model we are building up
+    INetworkPtr m_QuantizedNetwork;
 
-    StaticRangeVisitor* m_Ranges;           ///< Previously recorded min/max ranges per intermediate tensor
-    INetworkPtr m_QuantizedNetwork;         ///< Quantized version of the model we are building up
+    /// Mapping from input network guids to quantized network guids
+    std::unordered_map<LayerGuid, LayerGuid> m_OriginalToQuantizedGuidMap;
 
-    std::map<LayerGuid, LayerGuid> m_OldToNewGuidMap;  ///< Mapping from input network guids to quantized network guids
-    std::map<LayerGuid, IConnectableLayer*> m_GuidToLayerMap; ///< Mapping from guid to layer in quantized network
+    /// Mapping from guid to layer in quantized network
+    std::unordered_map<LayerGuid, IConnectableLayer*> m_QuantizedGuidToLayerMap;
 };
 
-} //namespace armnn
\ No newline at end of file
+} //namespace armnn
index 1986e42..258d499 100644 (file)
 namespace armnn
 {
 
+StaticRangeVisitor::StaticRangeVisitor(std::unordered_map<LayerGuid, MinMaxRanges>& guidToRangesMap)
+    : m_GuidToRangesMap(guidToRangesMap)
+{}
+
+StaticRangeVisitor::MinMaxRange StaticRangeVisitor::GetRange(LayerGuid guid, unsigned int idx) const
+{
+    auto search = m_GuidToRangesMap.find(guid);
+    if (search == m_GuidToRangesMap.end())
+    {
+        return DefaultRange();
+    }
+    return search->second.at(idx);
+}
+
 void StaticRangeVisitor::SetRange(const IConnectableLayer* layer, unsigned int outputIdx, float min, float max)
 {
     auto& ranges = m_GuidToRangesMap[layer->GetGuid()];
@@ -23,20 +37,10 @@ void StaticRangeVisitor::SetRange(const IConnectableLayer* layer, unsigned int o
     ranges[outputIdx] = std::make_pair(min, max);
 }
 
-StaticRangeVisitor::MinMaxRange StaticRangeVisitor::GetRange(LayerGuid guid, unsigned int idx) const
-{
-    auto found = m_GuidToRangesMap.find(guid);
-    if (found != m_GuidToRangesMap.end())
-    {
-        return found->second.at(idx);
-    }
-    return DefaultRange();
-}
-
-void StaticRangeVisitor::VisitAdditionLayer(const IConnectableLayer *layer, const char *name)
+void StaticRangeVisitor::VisitAdditionLayer(const IConnectableLayer* layer, const char* name)
 {
     SetRange(layer, 0, -20.f, 20.f);
-};
+}
 
 void StaticRangeVisitor::VisitBatchNormalizationLayer(const IConnectableLayer* layer,
                                                       const BatchNormalizationDescriptor& desc,
@@ -55,9 +59,9 @@ void StaticRangeVisitor::VisitBatchNormalizationLayer(const IConnectableLayer* l
     SetRange(layer, 0, -15.0f, 15.0f);
 }
 
-void StaticRangeVisitor::VisitActivationLayer(const IConnectableLayer *layer,
+void StaticRangeVisitor::VisitActivationLayer(const IConnectableLayerlayer,
                                               const ActivationDescriptor& activationDescriptor,
-                                              const char *name)
+                                              const charname)
 {
     switch (activationDescriptor.m_Function)
     {
@@ -83,4 +87,4 @@ void StaticRangeVisitor::VisitActivationLayer(const IConnectableLayer *layer,
     }
 }
 
-} //namespace armnn
\ No newline at end of file
+} //namespace armnn
index ed02fb5..ea27947 100644 (file)
@@ -8,9 +8,9 @@
 #include "LayerVisitorBase.hpp"
 
 #include <armnn/INetwork.hpp>
+#include <armnn/INetworkQuantizer.hpp>
 
-#include <map>
-#include <vector>
+#include <unordered_map>
 
 namespace armnn
 {
@@ -18,15 +18,16 @@ namespace armnn
 /// Visitor class to establish min/max ranges based on the type of the layer
 class StaticRangeVisitor : public LayerVisitorBase<VisitorNoThrowPolicy>
 {
+private:
+    using MinMaxRange  = std::pair<float, float>;
+    using MinMaxRanges = std::vector<MinMaxRange>;
+
 public:
-    StaticRangeVisitor() = default;
+    StaticRangeVisitor(std::unordered_map<LayerGuid, MinMaxRanges>& guidToRangesMap);
     ~StaticRangeVisitor() = default;
 
-    using MinMaxRange = std::pair<float, float>;
-    using MinMaxRanges = std::vector<MinMaxRange>;
-
     /// Functions to set the Range on a per-layer-type basis
-    void VisitAdditionLayer(const IConnectableLayer *layer, const char *name = nullptr) override;
+    void VisitAdditionLayer(const IConnectableLayer* layer, const char* name = nullptr) override;
     void VisitBatchNormalizationLayer(const IConnectableLayer* layer,
                                       const BatchNormalizationDescriptor& desc,
                                       const ConstTensor& mean,
@@ -34,22 +35,22 @@ public:
                                       const ConstTensor& beta,
                                       const ConstTensor& gamma,
                                       const char* name = nullptr) override;
-    void VisitActivationLayer(const IConnectableLayer *layer,
+    void VisitActivationLayer(const IConnectableLayerlayer,
                               const ActivationDescriptor& activationDescriptor,
-                              const char *name = nullptr) override;
+                              const charname = nullptr) override;
 
-    /// Retreive the default range
+    /// Retrieve the default range
     MinMaxRange DefaultRange() const { return std::make_pair(-15.0f, 15.0f); }
 
-    /// Retreive the Range for a particular output slot on a particular layer
+    /// Retrieve the Range for a particular output slot on a particular layer
     MinMaxRange GetRange(LayerGuid guid, unsigned int idx) const;
 
 private:
     /// Set the range for an output slot on a layer
     void SetRange(const IConnectableLayer* layer, unsigned int outputIdx, float min, float max);
 
-    /// Mapping from Guid to an array of ranges for outputs
-    std::map<LayerGuid, MinMaxRanges> m_GuidToRangesMap;
+    /// Mapping from a layer Guid to an array of ranges for outputs
+    std::unordered_map<LayerGuid, MinMaxRanges>& m_GuidToRangesMap;
 };
 
-} //namespace armnn
\ No newline at end of file
+} //namespace armnn
index 6f9ad31..7f782dc 100644 (file)
 #include "../LayerVisitorBase.hpp"
 #include "../Network.hpp"
 #include "../Graph.hpp"
+#include "../NetworkQuantizerUtils.hpp"
+#include "../OverrideInputRangeVisitor.hpp"
 
 #include <boost/test/unit_test.hpp>
 
+#include <unordered_map>
+
 namespace armnn
 {
+using MinMaxRange = std::pair<float, float>;
+using MinMaxRanges = std::vector<MinMaxRange>;
+using MinMaxRangeMap = std::unordered_map<LayerGuid, MinMaxRanges>;
+
 BOOST_AUTO_TEST_SUITE(Quantizer)
 
 class TestQuantization : public LayerVisitorBase<VisitorThrowingPolicy>
@@ -44,12 +52,9 @@ public:
 void VisitLayersTopologically(const INetwork* inputNetwork, ILayerVisitor& visitor)
 {
     auto network = boost::polymorphic_downcast<const Network*>(inputNetwork);
-
     auto graph = network->GetGraph().TopologicalSort();
-    for (auto layer : graph)
-    {
-        layer->Accept(visitor);
-    }
+
+    VisitLayers(graph, visitor);
 }
 
 BOOST_AUTO_TEST_CASE(QuantizeAddition)
@@ -370,5 +375,89 @@ BOOST_AUTO_TEST_CASE(QuantizeBatchNorm)
     VisitLayersTopologically(quantizedNetwork.get(), validator);
 }
 
+BOOST_AUTO_TEST_CASE(OverrideInputRangeEmptyNetwork)
+{
+    MinMaxRangeMap guidToRangesMap; // Empty map of ranges
+    MinMaxRange minMaxRange(-12.3f, 45.6f); // Range to use for the override
+
+    Network network; // Empty network
+    auto inputLayers = network.GetGraph().GetInputLayers(); // Empty list of input layers
+
+    OverrideInputRangeVisitor overrideInputRangeVisitor(guidToRangesMap, 0, minMaxRange);
+    VisitLayers(inputLayers, overrideInputRangeVisitor);
+
+    BOOST_CHECK(guidToRangesMap.empty()); // Check that the map of ranges remained untouched
+}
+
+BOOST_AUTO_TEST_CASE(OverrideInputRangeNoInputLayers)
+{
+    MinMaxRangeMap guidToRangesMap; // Empty map of ranges
+    MinMaxRange minMaxRange(-12.3f, 45.6f); // Range to use for the override
+
+    Network network;
+    network.AddAdditionLayer(); // Network with no input layers
+    auto inputLayers = network.GetGraph().GetInputLayers(); // Empty list of input layers
+
+    OverrideInputRangeVisitor overrideInputRangeVisitor(guidToRangesMap, 0, minMaxRange);
+    VisitLayers(inputLayers, overrideInputRangeVisitor);
+
+    BOOST_CHECK(guidToRangesMap.empty()); // Check that the map of ranges remained untouched
+}
+
+BOOST_AUTO_TEST_CASE(OverrideInputRangeInputLayers)
+{
+    MinMaxRangeMap guidToRangesMap; // Empty map of ranges
+    MinMaxRange minMaxRange(-12.3f, 45.6f); // Range to use for the override
+
+    Network network;
+
+    // Adding the layers
+    IConnectableLayer* input0 = network.AddInputLayer(0);
+    IConnectableLayer* input1 = network.AddInputLayer(1);
+    IConnectableLayer* addition = network.AddAdditionLayer();
+    IConnectableLayer* output = network.AddOutputLayer(2);
+
+    // Connecting the layer
+    input0->GetOutputSlot(0).Connect(addition->GetInputSlot(0));
+    input1->GetOutputSlot(0).Connect(addition->GetInputSlot(1));
+    addition->GetOutputSlot(0).Connect(output->GetInputSlot(0));
+
+    // Setting the TensorInfos
+    TensorShape shape{1U};
+    TensorInfo info(shape, DataType::Float32);
+    input0->GetOutputSlot(0).SetTensorInfo(info);
+    input1->GetOutputSlot(0).SetTensorInfo(info);
+    addition->GetOutputSlot(0).SetTensorInfo(info);
+
+    auto inputLayers = network.GetGraph().GetInputLayers(); // List of input layers
+
+    // Trying to override the input range for the input layer with binding id 3 (does not exist in the network)
+    OverrideInputRangeVisitor overrideInputRangeVisitorLayer3(guidToRangesMap, 3, minMaxRange);
+    VisitLayers(inputLayers, overrideInputRangeVisitorLayer3);
+
+    // Check that the map of ranges remained untouched
+    BOOST_CHECK(guidToRangesMap.empty());
+
+    // Override the input range for the input layer with binding id 1
+    OverrideInputRangeVisitor overrideInputRangeVisitorLayer1(guidToRangesMap, 1, minMaxRange);
+    VisitLayers(inputLayers, overrideInputRangeVisitorLayer1);
+
+    // Check that the map of ranges has been populated
+    BOOST_CHECK(!guidToRangesMap.empty());
+
+    // Check that an entry for the input layer with binding id 0 does not exist
+    BOOST_CHECK(guidToRangesMap.find(input0->GetGuid()) == guidToRangesMap.end());
+
+    // Check that an entry for the input layer with binding id 1 exists
+    BOOST_CHECK(guidToRangesMap.find(input1->GetGuid()) != guidToRangesMap.end());
+
+    // Check that at least a value has been added for the input layer with binding id 1
+    BOOST_CHECK(!guidToRangesMap[input1->GetGuid()].empty());
+
+    // Check the the overridden values are what we intended to set
+    BOOST_CHECK(guidToRangesMap[input1->GetGuid()].at(0).first  == minMaxRange.first);
+    BOOST_CHECK(guidToRangesMap[input1->GetGuid()].at(0).second == minMaxRange.second);
+}
+
 BOOST_AUTO_TEST_SUITE_END()
-} //namespace armnn
+} // namespace armnn