IVGCVSW-2834 Add dynamic quantization via datasets

* Add QuantizationDataSet class for quantization data parsed from CSV file
* Add QuantizationInput for retrieving quantization data for each layer ID
* Add unit tests for command line processor and QuantizationDataSet

Change-Id: Iaf0a747b5f25a59a766ac04f7158e8cb7909d179
Signed-off-by: Nina Drozd <nina.drozd@arm.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6dc1a1f..34dbd91 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -138,12 +138,37 @@
         message(ERROR, "In order to build the ArmNN Quantization Tool you must set BUILD_ARMNN_SERIALIZER = YES")
     endif()
 
-    add_executable_ex(ArmnnQuantizer
+    set(armnn_quantizer_sources)
+    list(APPEND armnn_quantizer_sources
+        src/armnnQuantizer/QuantizationDataSet.hpp
+        src/armnnQuantizer/QuantizationDataSet.cpp
+        src/armnnQuantizer/QuantizationInput.hpp
+        src/armnnQuantizer/QuantizationInput.cpp
         src/armnnQuantizer/CommandLineProcessor.hpp
         src/armnnQuantizer/CommandLineProcessor.cpp
+        )
+
+    add_library_ex(armnnQuantizer SHARED ${armnn_quantizer_sources})
+
+    target_include_directories(armnnQuantizer PRIVATE include/armnnDeserializer)
+    target_include_directories(armnnQuantizer PRIVATE src/armnnUtils)
+    target_include_directories(armnnQuantizer PRIVATE src/armnn)
+
+    include_directories(SYSTEM "${FLATBUFFERS_INCLUDE_PATH}")
+    set_target_properties(armnnQuantizer PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
+
+    target_link_libraries(armnnQuantizer
+            ${Boost_SYSTEM_LIBRARY}
+            ${Boost_PROGRAM_OPTIONS_LIBRARY}
+            ${Boost_FILESYSTEM_LIBRARY}
+            ${Boost_LOG_LIBRARY}
+            ${Boost_THREAD_LIBRARY} )
+
+    add_executable_ex(ArmnnQuantizer
         src/armnnQuantizer/ArmNNQuantizerMain.cpp)
 
     target_include_directories(ArmnnQuantizer PRIVATE include/armnnDeserializer)
+    target_include_directories(ArmnnQuantizer PRIVATE src/armnn)
 
     target_link_libraries(ArmnnQuantizer
             ${Boost_SYSTEM_LIBRARY}
@@ -153,8 +178,10 @@
             ${Boost_THREAD_LIBRARY} )
 
     target_link_libraries(ArmnnQuantizer
+            armnnQuantizer
             armnnSerializer
             armnn
+            armnnUtils
             ${FLATBUFFERS_LIBRARY})
 
     if(Threads_FOUND AND (NOT ("${CMAKE_SYSTEM_NAME}" STREQUAL Android)))
@@ -554,6 +581,12 @@
             )
     endif()
 
+    if(BUILD_ARMNN_QUANTIZER)
+        list(APPEND unittest_sources
+             src/armnnQuantizer/test/QuantizationDataSetTests.cpp
+             )
+    endif()
+
     if(BUILD_ARMNN_SERIALIZER)
         enable_language(ASM)
         list(APPEND unittest_sources
@@ -652,6 +685,12 @@
         target_link_libraries(UnitTests armnnSerializer)
     endif()
 
+    if(BUILD_ARMNN_QUANTIZER)
+        target_include_directories(UnitTests SYSTEM PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src/armnnQuantizer)
+        target_include_directories(UnitTests SYSTEM PRIVATE "${FLATBUFFERS_INCLUDE_PATH}")
+        target_link_libraries(UnitTests armnnQuantizer armnnSerializer)
+    endif()
+
     if(BUILD_ONNX_PARSER)
         target_link_libraries(UnitTests armnnOnnxParser)
     endif()
diff --git a/include/armnn/INetworkQuantizer.hpp b/include/armnn/INetworkQuantizer.hpp
index 54c1c88..89548d1 100644
--- a/include/armnn/INetworkQuantizer.hpp
+++ b/include/armnn/INetworkQuantizer.hpp
@@ -7,6 +7,7 @@
 
 #include <armnn/INetwork.hpp>
 #include <armnn/Types.hpp>
+#include <armnn/Tensor.hpp>
 
 namespace armnn
 {
@@ -37,6 +38,9 @@
     /// Overrides the default quantization values for the input layer with the given id
     virtual void OverrideInputRange(LayerBindingId layerId, float min, float max) = 0;
 
+    /// Refine input network with a set of refinement data for specified LayerBindingId
+    virtual void Refine(const InputTensors& inputTensors) = 0;
+
     /// Extract final quantized network
     virtual INetworkPtr ExportNetwork() = 0;
 
diff --git a/src/armnn/NetworkQuantizer.cpp b/src/armnn/NetworkQuantizer.cpp
index f577aea..4692a68 100644
--- a/src/armnn/NetworkQuantizer.cpp
+++ b/src/armnn/NetworkQuantizer.cpp
@@ -49,6 +49,11 @@
     VisitLayers(inputLayers, overrideInputRangeVisitor);
 }
 
+void NetworkQuantizer::Refine(const InputTensors& inputTensors)
+{
+    //Implementation in a following commit
+}
+
 INetworkPtr NetworkQuantizer::ExportNetwork()
 {
     const Graph& graph = boost::polymorphic_downcast<const Network*>(m_InputNetwork)->GetGraph().TopologicalSort();
diff --git a/src/armnn/NetworkQuantizer.hpp b/src/armnn/NetworkQuantizer.hpp
index 5e93f70..4f6359f 100644
--- a/src/armnn/NetworkQuantizer.hpp
+++ b/src/armnn/NetworkQuantizer.hpp
@@ -21,6 +21,7 @@
     : m_InputNetwork(inputNetwork), m_Options(options) {}
 
     void OverrideInputRange(LayerBindingId layerId, float min, float max) override;
+    void Refine(const InputTensors& inputTensors) override;
     INetworkPtr ExportNetwork() override;
 
 private:
diff --git a/src/armnnQuantizer/ArmNNQuantizerMain.cpp b/src/armnnQuantizer/ArmNNQuantizerMain.cpp
index 9ac8966..103597a 100644
--- a/src/armnnQuantizer/ArmNNQuantizerMain.cpp
+++ b/src/armnnQuantizer/ArmNNQuantizerMain.cpp
@@ -7,6 +7,8 @@
 #include <armnnDeserializer/IDeserializer.hpp>
 #include <armnn/INetworkQuantizer.hpp>
 #include <armnnSerializer/ISerializer.hpp>
+#include "QuantizationDataSet.hpp"
+#include "QuantizationInput.hpp"
 
 #include <algorithm>
 #include <fstream>
@@ -41,31 +43,32 @@
     armnn::INetworkPtr network = parser->CreateNetworkFromBinary(binaryContent);
     armnn::INetworkQuantizerPtr quantizer = armnn::INetworkQuantizer::Create(network.get(), quantizerOptions);
 
-    std::string csvFileName = cmdline.GetCsvFileName();
-    if (csvFileName != "")
+    if (cmdline.HasQuantizationData())
     {
-        // Call the Quantizer::Refine() function which will update the min/max ranges for the quantize constants
-        std::ifstream csvFileStream(csvFileName);
-        std::string line;
-        std::string csvDirectory = cmdline.GetCsvFileDirectory();
-        while(getline(csvFileStream, line))
+        armnnQuantizer::QuantizationDataSet dataSet = cmdline.GetQuantizationDataSet();
+        if (!dataSet.IsEmpty())
         {
-            std::istringstream s(line);
-            std::vector<std::string> row;
-            std::string entry;
-            while(getline(s, entry, ','))
+            // Get the Input Tensor Infos
+            armnnQuantizer::InputLayerVisitor inputLayerVisitor;
+            network->Accept(inputLayerVisitor);
+
+            for(armnnQuantizer::QuantizationInput quantizationInput : dataSet)
             {
-                entry.erase(std::remove(entry.begin(), entry.end(), ' '), entry.end());
-                entry.erase(std::remove(entry.begin(), entry.end(), '"'), entry.end());
-                row.push_back(entry);
+                armnn::InputTensors inputTensors;
+                std::vector<std::vector<float>> inputData(quantizationInput.GetNumberOfInputs());
+                std::vector<armnn::LayerBindingId> layerBindingIds = quantizationInput.GetLayerBindingIds();
+                unsigned int count = 0;
+                for (armnn::LayerBindingId layerBindingId : quantizationInput.GetLayerBindingIds())
+                {
+                    armnn::TensorInfo tensorInfo = inputLayerVisitor.GetTensorInfo(layerBindingId);
+                    inputData[count] = quantizationInput.GetDataForEntry(layerBindingId);
+                    armnn::ConstTensor inputTensor(tensorInfo, inputData[count].data());
+                    inputTensors.push_back(std::make_pair(layerBindingId, inputTensor));
+                    count++;
+                }
+                quantizer->Refine(inputTensors);
             }
-            std::string rawFileName = cmdline.GetCsvFileDirectory() + "/" + row[2];
-            // passId: row[0]
-            // bindingId: row[1]
-            // rawFileName: file contains the RAW input tensor data
-            // LATER: Quantizer::Refine() function will be called with those arguments when it is implemented
         }
-        csvFileStream.close();
     }
 
     armnn::INetworkPtr quantizedNetwork = quantizer->ExportNetwork();
diff --git a/src/armnnQuantizer/CommandLineProcessor.cpp b/src/armnnQuantizer/CommandLineProcessor.cpp
index 16afe28..4f0d989 100644
--- a/src/armnnQuantizer/CommandLineProcessor.cpp
+++ b/src/armnnQuantizer/CommandLineProcessor.cpp
@@ -149,6 +149,9 @@
             boost::filesystem::path csvFilePath(m_CsvFileName);
             m_CsvFileDirectory = csvFilePath.parent_path().c_str();
         }
+
+        // If CSV file is defined, create a QuantizationDataSet for specified CSV file.
+        m_QuantizationDataSet = QuantizationDataSet(m_CsvFileName);
     }
 
     if (!armnnQuantizer::ValidateOutputDirectory(m_OutputDirectory))
@@ -158,7 +161,7 @@
 
     std::string output(m_OutputDirectory);
     output.append(m_OutputFileName);
-    
+
     if (boost::filesystem::exists(output))
     {
         std::cerr << "Output file [" << output << "] already exists" << std::endl;
diff --git a/src/armnnQuantizer/CommandLineProcessor.hpp b/src/armnnQuantizer/CommandLineProcessor.hpp
index 7e366a7..ae39abb 100644
--- a/src/armnnQuantizer/CommandLineProcessor.hpp
+++ b/src/armnnQuantizer/CommandLineProcessor.hpp
@@ -6,6 +6,8 @@
 
 #include <string>
 #include <iostream>
+#include <vector>
+#include "QuantizationDataSet.hpp"
 
 namespace armnnQuantizer
 {
@@ -31,13 +33,17 @@
     std::string GetOutputDirectoryName() {return m_OutputDirectory;}
     std::string GetOutputFileName() {return m_OutputFileName;}
     std::string GetQuantizationScheme() {return m_QuantizationScheme;}
-private:
+    QuantizationDataSet GetQuantizationDataSet() {return m_QuantizationDataSet;}
+    bool HasQuantizationData() {return !m_QuantizationDataSet.IsEmpty();}
+
+protected:
     std::string m_InputFileName;
     std::string m_CsvFileName;
     std::string m_CsvFileDirectory;
     std::string m_OutputDirectory;
     std::string m_OutputFileName;
     std::string m_QuantizationScheme;
+    QuantizationDataSet m_QuantizationDataSet;
 };
 
 } // namespace armnnQuantizer
diff --git a/src/armnnQuantizer/QuantizationDataSet.cpp b/src/armnnQuantizer/QuantizationDataSet.cpp
new file mode 100644
index 0000000..d225883
--- /dev/null
+++ b/src/armnnQuantizer/QuantizationDataSet.cpp
@@ -0,0 +1,165 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include "QuantizationDataSet.hpp"
+#include "CsvReader.hpp"
+
+#define BOOST_FILESYSTEM_NO_DEPRECATED
+
+#include <boost/filesystem/operations.hpp>
+#include <boost/filesystem/path.hpp>
+
+namespace armnnQuantizer
+{
+
+QuantizationDataSet::QuantizationDataSet()
+{
+}
+
+QuantizationDataSet::QuantizationDataSet(const std::string csvFilePath):
+    m_QuantizationInputs(),
+    m_CsvFilePath(csvFilePath)
+{
+    ParseCsvFile();
+}
+
+void AddInputData(unsigned int passId,
+                  armnn::LayerBindingId bindingId,
+                  const std::string& inputFilePath,
+                  std::map<unsigned int, QuantizationInput>& passIdToQuantizationInput)
+{
+    auto iterator = passIdToQuantizationInput.find(passId);
+    if (iterator == passIdToQuantizationInput.end())
+    {
+        QuantizationInput input(passId, bindingId, inputFilePath);
+        passIdToQuantizationInput.emplace(passId, input);
+    }
+    else
+    {
+        auto existingQuantizationInput = iterator->second;
+        existingQuantizationInput.AddEntry(bindingId, inputFilePath);
+    }
+}
+
+QuantizationDataSet::~QuantizationDataSet()
+{
+}
+
+void InputLayerVisitor::VisitInputLayer(const armnn::IConnectableLayer* layer,
+                                        armnn::LayerBindingId id,
+                                        const char* name)
+{
+    m_TensorInfos.emplace(id, layer->GetInputSlot(0).GetConnection()->GetTensorInfo());
+}
+
+armnn::TensorInfo InputLayerVisitor::GetTensorInfo(armnn::LayerBindingId layerBindingId)
+{
+    auto iterator = m_TensorInfos.find(layerBindingId);
+    if (iterator != m_TensorInfos.end())
+    {
+        return m_TensorInfos.at(layerBindingId);
+    }
+    else
+    {
+        throw armnn::Exception("Could not retrieve tensor info for binding ID " + std::to_string(layerBindingId));
+    }
+}
+
+
+unsigned int GetPassIdFromCsvRow(std::vector<armnnUtils::CsvRow> csvRows, unsigned int rowIndex)
+{
+    unsigned int passId;
+    try
+    {
+        passId = static_cast<unsigned int>(std::stoi(csvRows[rowIndex].values[0]));
+    }
+    catch (std::invalid_argument)
+    {
+        throw armnn::ParseException("Pass ID [" + csvRows[rowIndex].values[0] + "]" +
+                                    " is not correct format on CSV row " + std::to_string(rowIndex));
+    }
+    return passId;
+}
+
+armnn::LayerBindingId GetBindingIdFromCsvRow(std::vector<armnnUtils::CsvRow> csvRows, unsigned int rowIndex)
+{
+    armnn::LayerBindingId bindingId;
+    try
+    {
+        bindingId = std::stoi(csvRows[rowIndex].values[1]);
+    }
+    catch (std::invalid_argument)
+    {
+        throw armnn::ParseException("Binding ID [" + csvRows[rowIndex].values[0] + "]" +
+                                    " is not correct format on CSV row " + std::to_string(rowIndex));
+    }
+    return bindingId;
+}
+
+std::string GetFileNameFromCsvRow(std::vector<armnnUtils::CsvRow> csvRows, unsigned int rowIndex)
+{
+    std::string fileName = csvRows[rowIndex].values[2];
+
+    if (!boost::filesystem::exists(fileName))
+    {
+        throw armnn::ParseException("File [ " + fileName + "] provided on CSV row " + std::to_string(rowIndex) +
+                                    " does not exist.");
+    }
+
+    if (fileName.empty())
+    {
+        throw armnn::ParseException("Filename cannot be empty on CSV row " + std::to_string(rowIndex));
+    }
+    return fileName;
+}
+
+
+void QuantizationDataSet::ParseCsvFile()
+{
+    std::map<unsigned int, QuantizationInput> passIdToQuantizationInput;
+    armnnUtils::CsvReader reader;
+
+    if (m_CsvFilePath == "")
+    {
+        throw armnn::Exception("CSV file not specified.");
+    }
+
+    // Parse CSV file and extract data
+    std::vector<armnnUtils::CsvRow> csvRows = reader.ParseFile(m_CsvFilePath);
+    if (csvRows.empty())
+    {
+        throw armnn::Exception("CSV file [" + m_CsvFilePath + "] is empty.");
+    }
+
+    for (unsigned int i = 0; i < csvRows.size(); ++i)
+    {
+        if (csvRows[i].values.size() != 3)
+        {
+            throw armnn::Exception("CSV file [" + m_CsvFilePath + "] does not have correct number of entries " +
+                                   "on line " + std::to_string(i) + ". Expected 3 entries " +
+                                   "but was " + std::to_string(csvRows[i].values.size()));
+        }
+
+        unsigned int passId = GetPassIdFromCsvRow(csvRows, i);
+        armnn::LayerBindingId bindingId = GetBindingIdFromCsvRow(csvRows, i);
+        std::string rawFileName = GetFileNameFromCsvRow(csvRows, i);
+
+        AddInputData(passId, bindingId, rawFileName, passIdToQuantizationInput);
+    }
+
+    if (passIdToQuantizationInput.empty())
+    {
+        throw armnn::Exception("Could not parse CSV file.");
+    }
+
+    // Once all entries in CSV file are parsed successfully and QuantizationInput map is populated, populate
+    // QuantizationInputs iterator for easier access and clear the map
+    for (auto itr = passIdToQuantizationInput.begin(); itr != passIdToQuantizationInput.end(); ++itr)
+    {
+        m_QuantizationInputs.emplace_back(itr->second);
+    }
+}
+
+}
diff --git a/src/armnnQuantizer/QuantizationDataSet.hpp b/src/armnnQuantizer/QuantizationDataSet.hpp
new file mode 100644
index 0000000..3a97630
--- /dev/null
+++ b/src/armnnQuantizer/QuantizationDataSet.hpp
@@ -0,0 +1,55 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#pragma once
+
+#include <map>
+#include "QuantizationInput.hpp"
+#include "armnn/LayerVisitorBase.hpp"
+#include "armnn/Tensor.hpp"
+
+namespace armnnQuantizer
+{
+
+/// QuantizationDataSet is a structure which is created after parsing a quantization CSV file.
+/// It contains records of filenames which contain refinement data per pass ID for binding ID.
+class QuantizationDataSet
+{
+    using QuantizationInputs = std::vector<armnnQuantizer::QuantizationInput>;
+public:
+
+    using iterator = QuantizationInputs::iterator;
+    using const_iterator = QuantizationInputs::const_iterator;
+
+    QuantizationDataSet();
+    QuantizationDataSet(std::string csvFilePath);
+    ~QuantizationDataSet();
+    bool IsEmpty() const {return m_QuantizationInputs.empty();}
+
+    iterator begin() { return m_QuantizationInputs.begin(); }
+    iterator end() { return m_QuantizationInputs.end(); }
+    const_iterator begin() const { return m_QuantizationInputs.begin(); }
+    const_iterator end() const { return m_QuantizationInputs.end(); }
+    const_iterator cbegin() const { return m_QuantizationInputs.cbegin(); }
+    const_iterator cend() const { return m_QuantizationInputs.cend(); }
+
+private:
+    void ParseCsvFile();
+
+    QuantizationInputs m_QuantizationInputs;
+    std::string m_CsvFilePath;
+};
+
+/// Visitor class implementation to gather the TensorInfo for LayerBindingID for creation of ConstTensor for Refine.
+class InputLayerVisitor : public armnn::LayerVisitorBase<armnn::VisitorNoThrowPolicy>
+{
+public:
+    void VisitInputLayer(const armnn::IConnectableLayer *layer, armnn::LayerBindingId id, const char* name);
+    armnn::TensorInfo GetTensorInfo(armnn::LayerBindingId);
+private:
+    std::map<armnn::LayerBindingId, armnn::TensorInfo> m_TensorInfos;
+};
+
+} // namespace armnnQuantizer
\ No newline at end of file
diff --git a/src/armnnQuantizer/QuantizationInput.cpp b/src/armnnQuantizer/QuantizationInput.cpp
new file mode 100644
index 0000000..bb7aff1
--- /dev/null
+++ b/src/armnnQuantizer/QuantizationInput.cpp
@@ -0,0 +1,103 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include "QuantizationInput.hpp"
+
+#include <iostream>
+#include <fstream>
+#include <cstring>
+#include "armnn/Exceptions.hpp"
+
+namespace armnnQuantizer
+{
+
+QuantizationInput::QuantizationInput(const unsigned int passId,
+    const armnn::LayerBindingId bindingId,
+    const std::string fileName):
+    m_PassId(passId)
+{
+    m_LayerBindingIdToFileName.emplace(bindingId, fileName);
+}
+
+QuantizationInput::QuantizationInput(const QuantizationInput& other)
+{
+    m_PassId = other.GetPassId();
+    m_LayerBindingIdToFileName.clear();
+    for (armnn::LayerBindingId bindingId : other.GetLayerBindingIds())
+    {
+        std::string filename = other.GetFileName(bindingId);
+        AddEntry(bindingId, filename);
+    }
+}
+
+void QuantizationInput::AddEntry(const armnn::LayerBindingId bindingId, const std::string fileName)
+{
+    m_LayerBindingIdToFileName.emplace(bindingId, fileName);
+}
+
+std::vector<float> QuantizationInput::GetDataForEntry(const armnn::LayerBindingId bindingId) const
+{
+    if (m_LayerBindingIdToFileName.at(bindingId).empty())
+    {
+        throw armnn::Exception("Layer binding ID not found");
+    }
+
+    std::string fileName = m_LayerBindingIdToFileName.at(bindingId);
+    std::ifstream in(fileName.c_str(), std::ifstream::binary);
+    if (!in.is_open())
+    {
+        throw armnn::Exception("Failed to open input tensor file " + fileName);
+    }
+
+    std::string line;
+    std::vector<float> values;
+    char* pEnd;
+
+    while (std::getline(in, line, ' '))
+    {
+        values.emplace_back(std::strtof(line.c_str(), &pEnd));
+    }
+    return values;
+}
+
+std::vector<armnn::LayerBindingId> QuantizationInput::GetLayerBindingIds() const
+{
+    std::vector<armnn::LayerBindingId> layerBindingIDs;
+
+    for (auto iterator = m_LayerBindingIdToFileName.begin(); iterator != m_LayerBindingIdToFileName.end(); ++iterator)
+    {
+        layerBindingIDs.emplace_back(iterator->first);
+    }
+    return layerBindingIDs;
+}
+
+unsigned long QuantizationInput::GetNumberOfInputs() const
+{
+    return m_LayerBindingIdToFileName.size();
+}
+
+unsigned int QuantizationInput::GetPassId() const
+{
+    return m_PassId;
+}
+
+std::string QuantizationInput::GetFileName(const armnn::LayerBindingId bindingId) const
+{
+    auto iterator = m_LayerBindingIdToFileName.find(bindingId);
+    if (iterator != m_LayerBindingIdToFileName.end())
+    {
+        return m_LayerBindingIdToFileName.at(bindingId);
+    }
+    else
+    {
+        throw armnn::Exception("Could not retrieve filename for binding ID " + std::to_string(bindingId));
+    }
+}
+
+QuantizationInput::~QuantizationInput() noexcept
+{
+}
+
+}
\ No newline at end of file
diff --git a/src/armnnQuantizer/QuantizationInput.hpp b/src/armnnQuantizer/QuantizationInput.hpp
new file mode 100644
index 0000000..ebabdd7
--- /dev/null
+++ b/src/armnnQuantizer/QuantizationInput.hpp
@@ -0,0 +1,54 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#pragma once
+
+#include <map>
+#include <armnn/Types.hpp>
+#include <armnn/INetworkQuantizer.hpp>
+
+namespace armnnQuantizer
+{
+
+/// QuantizationInput for specific pass ID, can list a corresponding raw data file for each LayerBindingId.
+class QuantizationInput
+{
+public:
+
+    /// Constructor for QuantizationInput
+    QuantizationInput(const unsigned int passId,
+                      const armnn::LayerBindingId bindingId,
+                      const std::string fileName);
+
+    QuantizationInput(const QuantizationInput& other);
+
+    // Add binding ID to image tensor filepath entry
+    void AddEntry(const armnn::LayerBindingId bindingId, const std::string fileName);
+
+    // Retrieve tensor data for entry with provided binding ID
+    std::vector<float> GetDataForEntry(const armnn::LayerBindingId bindingId) const;
+
+    /// Retrieve Layer Binding IDs for this QuantizationInput.
+    std::vector<armnn::LayerBindingId> GetLayerBindingIds() const;
+
+    /// Get number of inputs for this QuantizationInput.
+    unsigned long GetNumberOfInputs() const;
+
+    /// Retrieve Pass ID for this QuantizationInput.
+    unsigned int GetPassId() const;
+
+    /// Retrieve filename path for specified Layer Binding ID.
+    std::string GetFileName(const armnn::LayerBindingId bindingId) const;
+
+    /// Destructor
+    ~QuantizationInput() noexcept;
+
+private:
+    unsigned int m_PassId;
+    std::map<armnn::LayerBindingId, std::string> m_LayerBindingIdToFileName;
+
+};
+
+}
\ No newline at end of file
diff --git a/src/armnnQuantizer/test/QuantizationDataSetTests.cpp b/src/armnnQuantizer/test/QuantizationDataSetTests.cpp
new file mode 100644
index 0000000..2b46aae
--- /dev/null
+++ b/src/armnnQuantizer/test/QuantizationDataSetTests.cpp
@@ -0,0 +1,144 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include <boost/test/unit_test.hpp>
+
+#include "../QuantizationDataSet.hpp"
+#include <iostream>
+#include <fstream>
+#include <vector>
+#include <map>
+
+#define BOOST_FILESYSTEM_NO_DEPRECATED
+
+#include <boost/filesystem/operations.hpp>
+#include <boost/filesystem/fstream.hpp>
+#include <boost/filesystem/path.hpp>
+#include <boost/optional/optional.hpp>
+
+
+using namespace armnnQuantizer;
+
+struct CsvTestHelper {
+
+    CsvTestHelper()
+    {
+        BOOST_TEST_MESSAGE("setup fixture");
+    }
+
+    ~CsvTestHelper()
+    {
+        BOOST_TEST_MESSAGE("teardown fixture");
+        TearDown();
+    }
+
+    std::string CreateTempCsvFile(std::map<int, std::vector<float>> csvData)
+    {
+        boost::filesystem::path fileDir = boost::filesystem::temp_directory_path();
+        boost::filesystem::path p{fileDir / boost::filesystem::unique_path("%%%%-%%%%-%%%%.csv")};
+
+        boost::filesystem::path tensorInput1{fileDir / boost::filesystem::unique_path("input_0_0.raw")};
+        boost::filesystem::path tensorInput2{fileDir / boost::filesystem::unique_path("input_1_0.raw")};
+        boost::filesystem::path tensorInput3{fileDir / boost::filesystem::unique_path("input_2_0.raw")};
+
+        try
+        {
+            boost::filesystem::ofstream ofs{p};
+
+            boost::filesystem::ofstream ofs1{tensorInput1};
+            boost::filesystem::ofstream ofs2{tensorInput2};
+            boost::filesystem::ofstream ofs3{tensorInput3};
+
+
+            for(auto entry : csvData.at(0))
+            {
+                ofs1 << entry << " ";
+            }
+            for(auto entry : csvData.at(1))
+            {
+                ofs2 << entry << " ";
+            }
+            for(auto entry : csvData.at(2))
+            {
+                ofs3 << entry << " ";
+            }
+
+            ofs << "0, 0, " << tensorInput1.c_str() << std::endl;
+            ofs << "2, 0, " << tensorInput3.c_str() << std::endl;
+            ofs << "1, 0, " << tensorInput2.c_str() << std::endl;
+
+            ofs.close();
+            ofs1.close();
+            ofs2.close();
+            ofs3.close();
+        }
+        catch (std::exception &e)
+        {
+            std::cerr << "Unable to write to file at location [" << p.c_str() << "] : " << e.what() << std::endl;
+            BOOST_TEST(false);
+        }
+
+        m_CsvFile = p;
+        return p.string();
+    }
+
+    void TearDown()
+    {
+       RemoveCsvFile();
+    }
+
+    void RemoveCsvFile()
+    {
+        if (m_CsvFile)
+        {
+            try
+            {
+                boost::filesystem::remove(*m_CsvFile);
+            }
+            catch (std::exception &e)
+            {
+                std::cerr << "Unable to delete file [" << *m_CsvFile << "] : " << e.what() << std::endl;
+                BOOST_TEST(false);
+            }
+        }
+    }
+
+    boost::optional<boost::filesystem::path> m_CsvFile;
+};
+
+
+BOOST_AUTO_TEST_SUITE(QuantizationDataSetTests)
+
+BOOST_FIXTURE_TEST_CASE(CheckDataSet, CsvTestHelper)
+{
+
+    std::map<int, std::vector<float>> csvData;
+    csvData.insert(std::pair<int, std::vector<float>>(0, { 0.111111f, 0.222222f, 0.333333f }));
+    csvData.insert(std::pair<int, std::vector<float>>(1, { 0.444444f, 0.555555f, 0.666666f }));
+    csvData.insert(std::pair<int, std::vector<float>>(2, { 0.777777f, 0.888888f, 0.999999f }));
+
+    std::string myCsvFile = CsvTestHelper::CreateTempCsvFile(csvData);
+    QuantizationDataSet dataSet(myCsvFile);
+    BOOST_TEST(!dataSet.IsEmpty());
+
+    int csvRow = 0;
+    for(armnnQuantizer::QuantizationInput input : dataSet)
+    {
+        BOOST_TEST(input.GetPassId() == csvRow);
+
+        BOOST_TEST(input.GetLayerBindingIds().size() == 1);
+        BOOST_TEST(input.GetLayerBindingIds()[0] == 0);
+        BOOST_TEST(input.GetDataForEntry(0).size() == 3);
+
+        // Check that QuantizationInput data for binding ID 0 corresponds to float values
+        // used for populating the CSV file using by QuantizationDataSet
+        BOOST_TEST(input.GetDataForEntry(0).at(0) == csvData.at(csvRow).at(0));
+        BOOST_TEST(input.GetDataForEntry(0).at(1) == csvData.at(csvRow).at(1));
+        BOOST_TEST(input.GetDataForEntry(0).at(2) == csvData.at(csvRow).at(2));
+        ++csvRow;
+    }
+}
+
+BOOST_AUTO_TEST_SUITE_END();
\ No newline at end of file