[nnc] Eliminated visitors from caffe frontend (#1788)
authorIvan Vagin/AI Tools Lab /SRR/Engineer/삼성전자 <ivan.vagin@samsung.com>
Mon, 15 Oct 2018 17:12:17 +0000 (20:12 +0300)
committerРоман Михайлович Русяев/AI Tools Lab /SRR/Staff Engineer/삼성전자 <r.rusyaev@samsung.com>
Mon, 15 Oct 2018 17:12:17 +0000 (20:12 +0300)
* Replace visitors with iterators in caffe frontend
* Collect unsupported layers
* Fix coding style
* Remove dump visitor utils

Signed-off-by: Ivan Vagin <ivan.vagin@samsung.com>
30 files changed:
contrib/nnc/driver/Driver.cpp
contrib/nnc/examples/caffe_frontend/model_dump.cpp
contrib/nnc/examples/tflite_frontend/sanity_check.cpp
contrib/nnc/include/passes/caffe_frontend/CaffeFrontend.h
contrib/nnc/include/passes/common_frontend/nn_importer.h
contrib/nnc/passes/caffe_frontend/caffe_dump_visitor.cpp [deleted file]
contrib/nnc/passes/caffe_frontend/caffe_dump_visitor.h [deleted file]
contrib/nnc/passes/caffe_frontend/caffe_frontend.cpp
contrib/nnc/passes/caffe_frontend/caffe_importer.cpp
contrib/nnc/passes/caffe_frontend/caffe_importer.h
contrib/nnc/passes/caffe_frontend/caffe_model_visitor.cpp [deleted file]
contrib/nnc/passes/caffe_frontend/caffe_model_visitor.h [deleted file]
contrib/nnc/passes/caffe_frontend/caffe_op_creator.cpp
contrib/nnc/passes/caffe_frontend/caffe_op_creator.h
contrib/nnc/passes/caffe_frontend/caffe_op_types.h [new file with mode: 0644]
contrib/nnc/passes/caffe_frontend/caffe_visitor.h [deleted file]
contrib/nnc/passes/caffe_frontend/caffe_walker.cpp [deleted file]
contrib/nnc/passes/caffe_frontend/caffe_walker.h [deleted file]
contrib/nnc/passes/caffe_frontend/proto_reader.cpp
contrib/nnc/passes/caffe_frontend/proto_reader.h
contrib/nnc/passes/tflite_frontend/tflite_frontend.cpp
contrib/nnc/passes/tflite_frontend/tflite_importer.inline.cpp
contrib/nnc/passes/tflite_frontend/tflite_importer.inline.h
contrib/nnc/passes/tflite_frontend/tflite_v3_importer.h
contrib/nnc/tests/import/caffe.cpp
contrib/nnc/tests/import/tflite.cpp
contrib/nnc/unittests/CMakeLists.txt
contrib/nnc/unittests/caffe_frontend/CMakeLists.txt [new file with mode: 0644]
contrib/nnc/unittests/caffe_frontend/test_data/unsupported.caffemodel [new file with mode: 0644]
contrib/nnc/unittests/caffe_frontend/unsupportedCaffeModel.cpp [new file with mode: 0644]

index 5a46c5f..4534277 100644 (file)
@@ -72,7 +72,7 @@ static void registerFrontendPass()
   if ( cli::caffeFrontend )
   {
 #ifdef NNC_FRONTEND_CAFFE_ENABLED
-    pass = &caffe::CaffeFrontend::getInstance();
+    pass = &CaffeFrontend::getInstance();
 #endif // NNC_FRONTEND_CAFFE_ENABLED
   }
   else if ( cli::tflFrontend )
index 55c8dde..7e587a5 100644 (file)
@@ -28,50 +28,23 @@ using namespace nnc;
 using namespace nnc::mir;
 using namespace nnc::cli;
 
-enum Format {FormatDot, FormatDump};
-
-static Option<bool> isDumpFormat(optname("--dump"),
-                                 overview("if not setted DOT format will be used"),
-                                 false,
-                                 optional(true));
-
-int main(int argc, const char **argv)
-{
+int main(int argc, const char **argv) {
   cli::CommandLine::getParser()->parseCommandLine(argc, argv, false);
   std::string model = cli::inputFile;
 
-  nnc::caffe::CaffeImporter importer{model};
+  nnc::CaffeImporter importer{model};
 
-  if (!importer.import())
-  {
-    std::cout << "Could not load model \"" << model << "\"" << std::endl;
-    return -1;
+  try {
+    importer.import();
+    IrDotDumper dotDumper;
+    ShapeInference inf;
+    auto g = static_cast<Graph *>(importer.createIR());
+    g->accept(&inf);
+    g->accept(&dotDumper);
+    dotDumper.writeDot(std::cout);
   }
-
-  Format format = isDumpFormat ? FormatDump : FormatDot;
-
-  switch (format)
-  {
-  case FormatDump:
-    importer.dump();
-    break;
-  case FormatDot:
-    try
-    {
-      IrDotDumper dotDumper;
-      ShapeInference inf;
-      auto g = static_cast<Graph *>(importer.createIR());
-      g->accept(&inf);
-      g->accept(&dotDumper);
-
-      dotDumper.writeDot(std::cout);
-    } catch (PassException &e) {
-      std::cout << "Error: " << e.what() << std::endl;
-      return -1;
-    }
-    break;
-  default:
-    std::cout << "Error: Unsuported format" << std::endl;
+  catch (PassException &e) {
+    std::cout << "Error: " << e.reason() << std::endl;
     return -1;
   }
 
index b104a03..cab2032 100644 (file)
@@ -30,48 +30,23 @@ using namespace nnc::cli;
 
 enum Format {FormatDot, FormatDump};
 
-static Option<bool> isDumpFormat(optname("--dump"),
-                                 overview("if not setted DOT format will be used"),
-                                 false,
-                                 optional(true));
-
-int main(int argc, const char **argv)
-{
+int main(int argc, const char **argv) {
   cli::CommandLine::getParser()->parseCommandLine(argc, argv, false);
   std::string model = cli::inputFile;
 
   nnc::tflite::v3::TfliteImporter importer{model};
 
-  if (!importer.import())
-  {
-    std::cout << "Could not load model \"" << model << "\"" << std::endl;
-    return -1;
+  try {
+    importer.import();
+    IrDotDumper dotDumper;
+    mir::ShapeInference inf;
+    auto g = static_cast<mir::Graph *>(importer.createIR());
+    g->accept(&inf);
+    g->accept(&dotDumper);
+    dotDumper.writeDot(std::cout);
   }
-
-  Format format = isDumpFormat ? FormatDump : FormatDot;
-
-  switch (format)
-  {
-  case FormatDump:
-    importer.dump();
-    break;
-  case FormatDot:
-    try
-    {
-      IrDotDumper dotDumper;
-      mir::ShapeInference inf;
-      auto g = static_cast<mir::Graph *>(importer.createIR());
-      g->accept(&inf);
-      g->accept(&dotDumper);
-
-      dotDumper.writeDot(std::cout);
-    } catch (PassException &e) {
-      std::cout << "Error: " << e.what() << std::endl;
-      return -1;
-    }
-    break;
-  default:
-    std::cout << "Error: Unsuported format" << std::endl;
+  catch (PassException &e) {
+    std::cout << "Error: " << e.what() << std::endl;
     return -1;
   }
 
index f75bd55..13e6c54 100644 (file)
 #include "pass/PassData.h"
 
 
-namespace nnc
-{
-namespace caffe
-{
+namespace nnc {
 
 /**
  * @brief class represent frontend of caffe NN framework
  */
-class CaffeFrontend : public Pass
-{
+class CaffeFrontend : public Pass {
 public:
   CaffeFrontend &operator=(const CaffeFrontend &) = delete;
   CaffeFrontend(const CaffeFrontend &) = delete;
@@ -42,7 +38,6 @@ public:
   PassData run(PassData data) override;
 };
 
-} // namespace caffe
 } // namespace nnc
 
 #endif //NNCC_CAFFEFRONTEND_H
index aa9fad4..099f52c 100644 (file)
 #ifndef FRONTEND_COMMON_INCLUDE_NN_IMPORTER_
 #define FRONTEND_COMMON_INCLUDE_NN_IMPORTER_
 
-namespace nnc
-{
+#include "core/modelIR/graph.h"
 
-class NNImporter
-{
+namespace nnc {
+
+class NNImporter {
 public:
   virtual ~NNImporter() = default;
 
-  virtual bool import() = 0;
-  virtual void *createIR() = 0;
-  virtual void dump() = 0;
+  virtual void import() = 0;
+  virtual mir::Graph *createIR() = 0;
 };
 
 } // namespace nnc
diff --git a/contrib/nnc/passes/caffe_frontend/caffe_dump_visitor.cpp b/contrib/nnc/passes/caffe_frontend/caffe_dump_visitor.cpp
deleted file mode 100644 (file)
index bb49089..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <iostream>
-
-#include "caffe_dump_visitor.h"
-
-namespace nnc
-{
-namespace caffe
-{
-
-using std::cout;
-using std::endl;
-
-std::ostream& operator<<(std::ostream& os, const BlobShape& bs)
-{
-    cout << "[";
-    for (int i = 0; i < bs.dim_size(); ++i)
-    {
-        if (i != 0)
-            cout << ", ";
-        cout << bs.dim(i);
-    }
-    cout << "]";
-
-    return os;
-}
-
-void DumpVisitor::visit(const NetParameter &np)
-{
-    cout << "[Network name]: " << np.name() << endl;
-    cout << "[Network layer_size]: " << np.layer_size() << endl;
-
-    if (np.layers_size() != 0)
-        cout << "[DEPRECATED, Network layers_size]: " << np.layers_size() << endl;
-    if (np.input_size() != 0)
-        cout << "[DEPRECATED, Network input_size]: " << np.input_size() << endl;
-    if (np.input_shape_size() != 0)
-        cout << "[DEPRECATED, Network input_shapes_size]: " << np.input_shape_size() << endl;
-    if (np.input_dim_size() != 0)
-        cout << "[DEPRECATED, Network input_dim_size]: " << np.input_dim_size() << endl;
-}
-
-void DumpVisitor::visit(const LayerParameter &lp)
-{
-    cout << "[" << lp.type() << "]: " << lp.name() << endl;
-
-    for (int i = 0; i < lp.bottom_size(); ++i)
-        cout << "    [Input]: " << lp.bottom(i) << endl;
-
-    for (int i = 0; i < lp.top_size(); ++i)
-        cout << "    [Output]: " << lp.top(i) << endl;;
-}
-
-void DumpVisitor::visit(const BlobProto &bp)
-{
-    if (bp.has_shape())
-    {
-        cout << "    [Blob shape]: ";
-        cout << bp.shape() << endl;
-    }
-}
-
-void DumpVisitor::visit(const BlobShape& bs)
-{
-    cout << "    [Blob shape]: ";
-    cout << bs << endl;
-}
-
-} // namespace caffe
-} // namespace nnc
diff --git a/contrib/nnc/passes/caffe_frontend/caffe_dump_visitor.h b/contrib/nnc/passes/caffe_frontend/caffe_dump_visitor.h
deleted file mode 100644 (file)
index ee88440..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef NNCC_CAFFE_DUMP_VISITOR_H
-#define NNCC_CAFFE_DUMP_VISITOR_H
-
-#include "caffe_visitor.h"
-
-namespace nnc
-{
-namespace caffe
-{
-
-class DumpVisitor : public Visitor
-{
-public:
-    void visit(const NetParameter &parameter) override;
-    void visit(const LayerParameter &parameter) override;
-    void visit(const BlobProto &proto) override;
-    void visit(const BlobShape &shape) override;
-};
-
-} // namespace caffe
-} // namespace nnc
-
-#endif //NNCC_CAFFE_DUMP_VISITOR_H
index 384874d..f511301 100644 (file)
 #include "caffe_importer.h"
 
 
-namespace nnc
-{
-namespace caffe
-{
+namespace nnc {
 
-Pass &CaffeFrontend::getInstance()
-{
+Pass &CaffeFrontend::getInstance() {
   static CaffeFrontend instance;
   return instance;
 }
 
-PassData CaffeFrontend::run(PassData data)
-{
+PassData CaffeFrontend::run(PassData data) {
   (void)data;
-  nnc::caffe::CaffeImporter importer{cli::inputFile};
+  nnc::CaffeImporter importer{cli::inputFile};
 
-  bool success = importer.import();
-
-  if (!success)
-  {
-    throw PassException("Could not load model: " + cli::inputFile + "\n");
-  }
+  importer.import();
 
   return reinterpret_cast<mir::Graph *>(importer.createIR());
 }
 
-} // namespace caffe
 } // namespace nnc
index b4c6709..eaa10e4 100644 (file)
  * limitations under the License.
  */
 
+#include <vector>
 #include <fstream>
 #include <sstream>
+#include <cassert>
 
-#include "caffe_walker.h"
-#include "caffe_model_visitor.h"
-#include "caffe_dump_visitor.h"
 #include "caffe_importer.h"
 #include "proto_reader.h"
 
-namespace nnc
-{
-namespace caffe
-{
+#include "core/modelIR/Shape.h"
+#include "core/modelIR/operations/variable_op.h"
+#include "core/modelIR/TensorUtil.h"
+#include "pass/PassException.h"
 
-bool CaffeImporter::import()
-{
-    GOOGLE_PROTOBUF_VERIFY_VERSION;
+#include "passes/common_frontend/shape_helper.h"
+
+namespace nnc {
 
-    net.reset(new NetParameter());
+using VariableOp = nnc::mir::ops::VariableOp;
+using nnc::mir::Shape;
+using nnc::mir::transposeTensor;
 
-    // import success flag is returned
-    return readProtoFromBinaryFile(modelFilename.c_str(), net.get());
+void CaffeImporter::import() {
+  GOOGLE_PROTOBUF_VERIFY_VERSION;
+
+  _net.reset(new NetParameter());
+  if (!readProtoFromBinaryFile(_modelFilename.c_str(), _net.get()))
+    throw PassException("Could not load model: " + _modelFilename + "\n");
+
+  collectUnsupportedLayers();
 }
 
-void *CaffeImporter::createIR()
-{
-    ModelVisitor irCreator;
-    ModelWalker caffeWalker(&irCreator);
+Graph *CaffeImporter::createIR() {
+
+  for (int i = 0; i < _net->layer_size(); ++i)
+    createMIRNodesFromLayer(_net->layer(i));
+
+  setIrNodeNames();
+  setGraphOutputs();
+
+  return _graph;
+}
+
+void CaffeImporter::collectUnsupportedLayers() {
+  processDeprecatedInput();
+
+  for (int i = 0; i < _net->layer_size(); ++i)
+    collectUnsupportedOp(_net->layer(i));
+
+  if (!_problemsOpSet.empty()) {
+    std::string msg("Detected problems:\n");
+    for (const auto& problemStr : _problemsOpSet)
+      msg.append(problemStr + "\n");
+    throw PassException(msg);
+  }
+}
+
+void CaffeImporter::createMIRNodesFromLayer(const LayerParameter& lp) {
+  auto inputs = createOpInputs(lp);
+  auto params = createOpParams(lp);
+
+  std::vector<INode::Ref> outputs;
+  INode *prev;
+  CaffeOpType opType = _operatorTypes.at(lp.type());
+
+  switch (opType) {
+    case CaffeOpType::input:
+      processInputLayer(lp);
+      break;
+    case CaffeOpType::convolution:
+      outputs = _opCreator.createConv2D(inputs, params, lp.convolution_param());
+      break;
+    case CaffeOpType::innerProduct:
+      outputs = _opCreator.createFullyConnected(inputs, params, lp.inner_product_param());
+      break;
+    case CaffeOpType::pooling:
+      outputs = _opCreator.createPool(inputs, params, lp.pooling_param());
+      break;
+    case CaffeOpType::concat:
+      outputs = _opCreator.createConcat(inputs, params, lp.concat_param());
+      break;
+    case CaffeOpType::reshape:
+      outputs = _opCreator.createReshape(inputs, params, lp.reshape_param());
+      break;
+    case CaffeOpType::ReLU:
+      outputs = _opCreator.createRelu(inputs, params, lp.relu_param());
+      break;
+    case CaffeOpType::softmax:
+      outputs = _opCreator.createSoftmax(inputs, params, lp.softmax_param());
+      break;
+    case CaffeOpType::scale:
+      outputs = _opCreator.createScale(inputs, params, lp.scale_param());
+      break;
+    case CaffeOpType::batchNorm:
+      outputs = _opCreator.createBatchNorm(inputs, params, lp.batch_norm_param());
+      break;
+    case CaffeOpType::dropout:
+      outputs = _opCreator.createDropout(inputs, params, lp.dropout_param());
+      break;
+    case CaffeOpType::split:
+      prev = _opsForBlobsTheyOutput[lp.bottom(0)];
+      for (int i = 0; i < lp.top_size(); ++i)
+        _opsForBlobsTheyOutput[lp.top(i)] = prev;
+      break;
+    default:
+      assert(false && "All unsupported types should have been found before this pass.");
+  }
+
+  for (auto item : outputs)
+    _opsForBlobsTheyOutput[lp.top(0)] = item;
+  _graphOutputs.assign(outputs.begin(), outputs.end());
+}
+
+void CaffeImporter::collectUnsupportedOp(const LayerParameter& lp) {
 
-    caffeWalker.walkNetParameter(*net);
-    irCreator.setIrNodeNames();
-    irCreator.setGraphOutputs();
+  auto it = _operatorTypes.find(lp.type());
+  if (it == _operatorTypes.end()) {
+    _problemsOpSet.insert(lp.type() + ": unknown layer");
+    return;
+  }
 
-    return irCreator.getGraph();
+  CaffeOpType opType = it->second;
+  std::vector<std::shared_ptr<IrTensor>> params;
+
+  switch (opType) {
+    case CaffeOpType::input:
+    case CaffeOpType::softmax:
+    case CaffeOpType::scale:
+    case CaffeOpType::dropout:
+    case CaffeOpType::split:
+    case CaffeOpType::concat:
+      // No checks
+      break;
+    case CaffeOpType::convolution:
+      OpCreator::checkConv2D(lp.convolution_param(), _problemsOpSet);
+      break;
+    case CaffeOpType::innerProduct:
+      OpCreator::checkFullyConnected(lp.inner_product_param(), _problemsOpSet);
+      break;
+    case CaffeOpType::pooling:
+      OpCreator::checkPool(lp.pooling_param(), _problemsOpSet);
+      break;
+    case CaffeOpType::reshape:
+      OpCreator::checkReshape(lp.reshape_param(), _problemsOpSet);
+      break;
+    case CaffeOpType::ReLU:
+      OpCreator::checkRelu(lp.relu_param(), _problemsOpSet);
+      break;
+    case CaffeOpType::batchNorm:
+      params = createOpParams(lp);
+      OpCreator::checkBatchNorm(lp.batch_norm_param(), params, _problemsOpSet);
+      break;
+    default:
+      _problemsOpSet.insert(lp.type() + ": unsupported layer");
+      break;
+  }
 }
 
-void CaffeImporter::dump()
+void CaffeImporter::createGraphInputs(const std::vector<std::string> &names,
+                                      const std::vector<Shape> &shapes) {
+    assert(names.size() == shapes.size());
+
+    for (size_t i = 0; i < names.size(); ++i) {
+        auto node = _graph->create<VariableOp>(names[i]);
+        _opsForBlobsTheyOutput[names[i]] = node;
+
+        Shape inputShape = shapes[i];
+        // WARNING! Temporary solution! Assuming that every 4D input will be used for a convolution,
+        // so we change every 4D input from Caffe NCHW to Model IR HWC (batch is cut off earlier).
+        // TODO: Implement a more consistent way of handling shapes within the model.
+        if (shapes[i].rank() == 3) {
+            const Shape &sh = shapes[i];
+            inputShape = Shape{sh.dim(1), sh.dim(2), sh.dim(0)};
+        }
+        // WARNING! Temporary solution!
+
+        node->getOperation()->setOutputShape(0, inputShape);
+    }
+}
+
+void CaffeImporter::processDeprecatedInput() {
+    if (_net->input_dim_size() != 0 || _net->input_shape_size() != 0)
+        throw PassException("Deprecated Caffe input types are not supported");
+}
+
+void CaffeImporter::processInputLayer(const LayerParameter& lp) {
+    std::vector<std::string> inputNames;
+    for (const auto &name  : lp.top())
+        inputNames.push_back(name);
+
+    for (const auto &shape : lp.input_param().shape()) {
+        Shape sh = ShapeHelper::createShape(shape.dim(), shape.dim_size());
+        _inputShapes.push_back(ShapeHelper::cutOffBatchDim(sh));
+    }
+
+    if (!_inputShapes.empty())
+        createGraphInputs(inputNames, _inputShapes);
+}
+
+std::shared_ptr<IrTensor> CaffeImporter::createTensor(const BlobProto &bp) {
+    auto type = IrTensor::DTYPE::FLOAT;
+    size_t elementSize;
+
+    const char *srcData;
+    size_t bufferSize;
+
+    if (bp.data_size() != 0) {
+        assert(bp.double_data_size() == 0);
+        elementSize = sizeof(float);
+        bufferSize = bp.data_size() * elementSize;
+        srcData = reinterpret_cast<const char *>(bp.data().data());
+    }
+    else if (bp.double_data_size() != 0) {
+        elementSize = sizeof(double);
+        bufferSize = bp.double_data_size() * elementSize;
+        srcData = reinterpret_cast<const char *>(bp.double_data().data());
+    }
+    else {
+        throw PassException("No data in Caffe BlobProto, investigate");
+    }
+
+    // Create untyped tensor. Note, tensor contents will be *copied* here.
+    std::shared_ptr<char> tensorBufferCopy(new char[bufferSize],
+                                           std::default_delete<char[]>());
+
+    char *dstData = tensorBufferCopy.get();
+    memcpy(dstData, srcData, bufferSize);
+
+    Shape tensorShape = ShapeHelper::createShape(
+        bp.shape().dim(), static_cast<size_t>(bp.shape().dim_size()));
+
+    auto tensor = std::make_shared<IrTensor>(tensorShape, tensorBufferCopy, type, elementSize);
+
+    return tensor;
+}
+
+std::vector<INode::Ref> CaffeImporter::createOpInputs(const LayerParameter &lp)
 {
-    DumpVisitor dumper;
-    ModelWalker caffeWalker(&dumper);
+    std::vector<INode::Ref> inputs;
 
-    caffeWalker.walkNetParameter(*net);
+    for (const auto &inputBlobName : lp.bottom())
+        inputs.push_back(_opsForBlobsTheyOutput[inputBlobName]);
+
+    return inputs;
 }
 
-} // namespace caffe
+/**
+ * @brief Prepares Caffe layer parameters for Model IR operation creator.
+ */
+std::vector<std::shared_ptr<IrTensor>> CaffeImporter::createOpParams(const LayerParameter &lp) {
+    std::vector<std::shared_ptr<IrTensor>> params;
+
+    for (const auto &blob : lp.blobs()) {
+
+        std::shared_ptr<IrTensor> tensor = createTensor(blob);
+
+        if (lp.has_convolution_param() && blob.shape().dim_size() == 4) {
+            // TODO support non default channel axis
+            assert(lp.convolution_param().axis() == 1 && "assuming channel axis number set to default");
+            params.emplace_back(transposeTensor<2, 3, 1, 0>(tensor));
+        }
+        else if (lp.has_inner_product_param() && blob.shape().dim_size() == 2) {
+            params.emplace_back(transposeTensor<1, 0>(tensor));
+        }
+        else {
+            params.push_back(tensor);
+        }
+    }
+
+    return params;
+}
+
+void CaffeImporter::setGraphOutputs() {
+    // Marking nodes as output nodes.
+    for (auto &outputIdx : _graphOutputs)
+        _graph->markOutput(outputIdx);
+}
+
+void CaffeImporter::setIrNodeNames() {
+    for (auto &item : _opsForBlobsTheyOutput)
+        item.second->setName(item.first);
+}
+
+const std::map<std::string, CaffeOpType> CaffeImporter::_operatorTypes = {
+    {"AbsVal", CaffeOpType::absVal},
+    {"Accuracy", CaffeOpType::accuracy},
+    {"ArgMax", CaffeOpType::argMax},
+    {"BatchNorm", CaffeOpType::batchNorm},
+    {"BatchReindex", CaffeOpType::batchReindex},
+    {"Bias", CaffeOpType::bias},
+    {"BNLL", CaffeOpType::BNLL},
+    {"Clip", CaffeOpType::clip},
+    {"Concat", CaffeOpType::concat},
+    {"ContrastiveLoss", CaffeOpType::contrastiveLoss},
+    {"Convolution", CaffeOpType::convolution},
+    {"Crop", CaffeOpType::crop},
+    {"Data", CaffeOpType::data},
+    {"Deconvolution", CaffeOpType::deconvolution},
+    {"Dropout", CaffeOpType::dropout},
+    {"DummyData", CaffeOpType::dummyData},
+    {"Eltwise", CaffeOpType::eltwise},
+    {"ELU", CaffeOpType::ELU},
+    {"Embed", CaffeOpType::embed},
+    {"EuclidianLoss", CaffeOpType::euclidianLoss},
+    {"Exp", CaffeOpType::exp},
+    {"Filter", CaffeOpType::filter},
+    {"Flatten", CaffeOpType::flatten},
+    {"HDF5Data", CaffeOpType::HDF5Data},
+    {"HDF5Output", CaffeOpType::HDF5Output},
+    {"HingeLoss", CaffeOpType::hingeLoss},
+    {"Im2Col", CaffeOpType::im2Col},
+    {"ImageData", CaffeOpType::imageData},
+    {"InfogainLoss", CaffeOpType::infogainLoss},
+    {"InnerProduct", CaffeOpType::innerProduct},
+    {"Input", CaffeOpType::input},
+    {"Log", CaffeOpType::log},
+    {"LRN", CaffeOpType::LRN},
+    {"LSTM", CaffeOpType::LSTM},
+    {"MemoryData", CaffeOpType::memoryData},
+    {"MultinomialLogisticLoss", CaffeOpType::multinomialLogisticLoss},
+    {"MVN", CaffeOpType::MVN},
+    {"Parameter", CaffeOpType::parameter},
+    {"Pooling", CaffeOpType::pooling},
+    {"Power", CaffeOpType::power},
+    {"PReLU", CaffeOpType::PReLU},
+    {"Python", CaffeOpType::python},
+    {"Recurrent", CaffeOpType::recurrent},
+    {"Reduction", CaffeOpType::reduction},
+    {"ReLU", CaffeOpType::ReLU},
+    {"Reshape", CaffeOpType::reshape},
+    {"RNN", CaffeOpType::RNN},
+    {"Scale", CaffeOpType::scale},
+    {"SigmoidCrossEntropyLoss", CaffeOpType::sigmoidCrossEntropyLoss},
+    {"Sigmoid", CaffeOpType::sigmoid},
+    {"Silence", CaffeOpType::silence},
+    {"Softmax", CaffeOpType::softmax},
+    {"SoftmaxLoss", CaffeOpType::softmaxLoss},
+    {"SPP", CaffeOpType::SPP},
+    {"Split", CaffeOpType::split},
+    {"Slice", CaffeOpType::slice},
+    {"Tanh", CaffeOpType::tanh},
+    {"Threshold", CaffeOpType::threshold},
+    {"Tile", CaffeOpType::tile},
+    {"WindowData", CaffeOpType::windowData}
+};
+
 } // namespace nnc
index d1f6298..cbec284 100644 (file)
@@ -1,3 +1,4 @@
+
 /*
  * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
  *
 #ifndef NNCC_CAFFE_IMPORTER_H
 #define NNCC_CAFFE_IMPORTER_H
 
+#include <set>
 #include <string>
 #include <memory>
 
 #include "caffe/proto/caffe.pb.h"
 
 #include "passes/common_frontend/nn_importer.h"
+#include "caffe_op_creator.h"
+#include "caffe_op_types.h"
 
-namespace nnc
-{
-namespace caffe
-{
+namespace nnc {
 
 using namespace ::caffe;
 
-class CaffeImporter : public NNImporter
-{
+class CaffeImporter : public NNImporter {
 public:
-    explicit CaffeImporter(std::string filename) : modelFilename(std::move(filename)) {};
+  explicit CaffeImporter(std::string filename) : _modelFilename(std::move(filename)),
+                                                 _graph(new mir::Graph()),
+                                                 _opCreator(_graph) {};
 
-    bool import() override;
-    void *createIR() override;
-    void dump() override;
+  void import() override;
+  Graph *createIR() override;
 
 private:
-    std::string modelFilename;
-    std::unique_ptr<NetParameter> net;
+  std::string _modelFilename;
+  std::unique_ptr<NetParameter> _net;
+  mir::Graph* _graph;
+  OpCreator _opCreator;
+
+  std::vector<mir::Shape> _inputShapes;
+  std::map<std::string, mir::INode::Ref> _opsForBlobsTheyOutput;
+  std::vector<mir::INode::Ref> _graphOutputs;
+
+  static const std::map<std::string, CaffeOpType> _operatorTypes;
+  std::set<std::string> _problemsOpSet{};
+
+  void setGraphOutputs();
+  void setIrNodeNames();
+
+  void collectUnsupportedLayers();
+  void createMIRNodesFromLayer(const LayerParameter& lp);
+  void collectUnsupportedOp(const LayerParameter& lp);
+  std::shared_ptr<mir::TensorVariant> createTensor(const ::caffe::BlobProto&);
+  std::vector<mir::INode::Ref> createOpInputs(const ::caffe::LayerParameter&);
+  std::vector<std::shared_ptr<mir::TensorVariant>> createOpParams(const ::caffe::LayerParameter&);
+
+  void createGraphInputs(const std::vector<std::string> &names,
+                         const std::vector<mir::Shape> &shapes);
+  void processInputLayer(const ::caffe::LayerParameter&);
+  void processDeprecatedInput();
 };
 
-} // namespace caffe
 } // namespace nnc
 
 #endif // NNCC_CAFFE_IMPORTER_H
diff --git a/contrib/nnc/passes/caffe_frontend/caffe_model_visitor.cpp b/contrib/nnc/passes/caffe_frontend/caffe_model_visitor.cpp
deleted file mode 100644 (file)
index cdad4a3..0000000
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <vector>
-#include <cassert>
-
-#include "core/modelIR/Shape.h"
-#include "core/modelIR/operations/variable_op.h"
-#include "core/modelIR/TensorUtil.h"
-#include "pass/PassException.h"
-
-#include "passes/common_frontend/shape_helper.h"
-#include "caffe_model_visitor.h"
-
-
-namespace nnc
-{
-namespace caffe
-{
-
-using VariableOp = nnc::mir::ops::VariableOp;
-using nnc::mir::Shape;
-using nnc::mir::transposeTensor;
-
-void ModelVisitor::visit(const NetParameter& np)
-{
-  processDeprecatedInput(np);
-}
-
-void ModelVisitor::visit(const LayerParameter& lp)
-{
-  auto inputs = createOpInputs(lp);
-  auto params = createOpParams(lp);
-
-  std::vector<INode::Ref> outputs;
-
-  // TODO: support other layer types
-
-  // This is the Input layer
-  if (lp.has_input_param())
-  {
-    processInputLayer(lp);
-  }
-  else if (lp.type() == "Convolution")
-  {
-    outputs = opCreator.createConv2D(inputs, params, lp.convolution_param());
-  }
-  else if (lp.has_inner_product_param())
-  {
-    outputs = opCreator.createFullyConnected(inputs, params, lp.inner_product_param());
-  }
-  else if (lp.has_pooling_param())
-  {
-    outputs = opCreator.createPool(inputs, params, lp.pooling_param());
-  }
-  else if (lp.has_concat_param() || lp.type() == "Concat")
-  {
-    outputs = opCreator.createConcat(inputs, params, lp.concat_param());
-  }
-  else if (lp.has_reshape_param())
-  {
-    outputs = opCreator.createReshape(inputs, params, lp.reshape_param());
-  }
-  else if (lp.has_relu_param() || lp.type() == "ReLU")
-  {
-    outputs = opCreator.createRelu(inputs, params, lp.relu_param());
-  }
-  else if (lp.has_softmax_param() || lp.type() == "Softmax")
-  {
-    outputs = opCreator.createSoftmax(inputs, params, lp.softmax_param());
-  }
-  else if (lp.has_scale_param())
-  {
-    outputs = opCreator.createScale(inputs, params, lp.scale_param());
-  }
-  else if (lp.has_batch_norm_param())
-  {
-    outputs = opCreator.createBatchNorm(inputs, params, lp.batch_norm_param());
-  }
-  else if (lp.has_dropout_param())
-  {
-    outputs = opCreator.createDropout(inputs, params, lp.dropout_param());
-  }
-  else if (lp.type() == "Split")
-  {
-    INode *prev = opsForBlobsTheyOutput[lp.bottom(0)];
-    for (int i = 0; i < lp.top_size(); ++i)
-    {
-      opsForBlobsTheyOutput[lp.top(i)] = prev;
-    }
-  }
-  else
-  {
-    throw PassException("Encountered unsupported Caffe layer type");
-  }
-
-  for (auto item : outputs)
-  {
-    opsForBlobsTheyOutput[lp.top(0)] = item;
-  }
-  graphOutputs.assign(outputs.begin(), outputs.end());
-}
-
-void ModelVisitor::visit(const BlobProto&) {}
-void ModelVisitor::visit(const BlobShape&) {}
-
-Graph *ModelVisitor::getGraph()
-{
-  return graph;
-}
-
-void ModelVisitor::createGraphInputs(const std::vector<std::string> &names,
-                                     const std::vector<Shape> &shapes)
-{
-  assert(names.size() == shapes.size());
-
-  for (size_t i = 0; i < names.size(); ++i)
-  {
-    auto node = graph->create<VariableOp>(names[i]);
-    opsForBlobsTheyOutput[names[i]] = node;
-
-    Shape inputShape = shapes[i];
-    // WARNING! Temporary solution! Assuming that every 4D input will be used for a convolution,
-    // so we change every 4D input from Caffe NCHW to Model IR HWC (batch is cut off earlier).
-    // TODO: Implement a more consistent way of handling shapes within the model.
-    if (shapes[i].rank() == 3)
-    {
-      const Shape &sh = shapes[i];
-      inputShape = Shape{sh.dim(1), sh.dim(2), sh.dim(0)};
-    }
-    // WARNING! Temporary solution!
-
-    node->getOperation()->setOutputShape(0, inputShape);
-  }
-}
-
-void ModelVisitor::processDeprecatedInput(const NetParameter& np)
-{
-  if (np.input_dim_size() != 0 || np.input_shape_size() != 0)
-  {
-    throw PassException("Deprecated Caffe input types are not supported");
-  }
-}
-
-void ModelVisitor::processInputLayer(const LayerParameter& lp)
-{
-  if (!inputShapes.empty())
-  {
-    throw std::runtime_error("Model contains both Input layer and deprecated input methods");
-  }
-
-  std::vector<std::string> inputNames;
-  for (const auto &name  : lp.top())
-  {
-    inputNames.push_back(name);
-  }
-
-  for (const auto &shape : lp.input_param().shape())
-  {
-    Shape sh = ShapeHelper::createShape(shape.dim(), shape.dim_size());
-    inputShapes.push_back(ShapeHelper::cutOffBatchDim(sh));
-  }
-
-  if (!inputShapes.empty())
-  {
-    createGraphInputs(inputNames, inputShapes);
-  }
-}
-
-std::shared_ptr<IrTensor> ModelVisitor::createTensor(const BlobProto &bp)
-{
-  IrTensor::DTYPE type = IrTensor::DTYPE::FLOAT;
-  size_t elementSize;
-
-  const char *srcData;
-  size_t bufferSize;
-
-  if (bp.data_size() != 0)
-  {
-    assert(bp.double_data_size() == 0);
-    elementSize = sizeof(float);
-    bufferSize = bp.data_size() * elementSize;
-    srcData = reinterpret_cast<const char *>(bp.data().data());
-  }
-  else if (bp.double_data_size() != 0)
-  {
-    elementSize = sizeof(double);
-    bufferSize = bp.double_data_size() * elementSize;
-    srcData = reinterpret_cast<const char *>(bp.double_data().data());
-  }
-  else
-  {
-    throw PassException("No data in Caffe BlobProto, investigate");
-  }
-
-  // Create untyped tensor. Note, tensor contents will be *copied* here.
-  std::shared_ptr<char> tensorBufferCopy(new char[bufferSize],
-                                         std::default_delete<char[]>());
-
-  char *dstData = tensorBufferCopy.get();
-  memcpy(dstData, srcData, bufferSize);
-
-  Shape tensorShape = ShapeHelper::createShape(
-          bp.shape().dim(), static_cast<size_t>(bp.shape().dim_size()));
-
-  auto tensor = std::make_shared<IrTensor>(tensorShape, tensorBufferCopy, type, elementSize);
-
-  return tensor;
-}
-
-std::vector<INode::Ref> ModelVisitor::createOpInputs(const LayerParameter &lp)
-{
-  std::vector<INode::Ref> inputs;
-
-  for (const auto &inputBlobName : lp.bottom())
-  {
-    inputs.push_back(opsForBlobsTheyOutput[inputBlobName]);
-  }
-
-  return inputs;
-}
-
-/**
- * @brief Prepares Caffe layer parameters for Model IR operation creator.
- */
-std::vector<std::shared_ptr<IrTensor>> ModelVisitor::createOpParams(const LayerParameter &lp)
-{
-  std::vector<std::shared_ptr<IrTensor>> params;
-
-  for (const auto &blob : lp.blobs())
-  {
-
-    std::shared_ptr<IrTensor> tensor = createTensor(blob);
-
-    if (lp.has_convolution_param() && blob.shape().dim_size() == 4)
-    {
-      // TODO support non default channel axis
-      assert(lp.convolution_param().axis() == 1 && "assuming channel axis number set to default");
-      params.emplace_back(transposeTensor<2, 3, 1, 0>(tensor));
-    }
-    else if (lp.has_inner_product_param() && blob.shape().dim_size() == 2)
-    {
-      params.emplace_back(transposeTensor<1, 0>(tensor));
-    }
-    else
-    {
-      params.push_back(tensor);
-    }
-  }
-
-  return params;
-}
-
-void ModelVisitor::setGraphOutputs() {
-  // Marking nodes as output nodes.
-  for (auto &outputIdx : graphOutputs)
-  {
-    graph->markOutput(outputIdx);
-  }
-}
-
-void ModelVisitor::setIrNodeNames() {
-  for (auto &item : opsForBlobsTheyOutput)
-  {
-    item.second->setName(item.first);
-  }
-}
-
-} // namespace caffe
-} // namespace nnc
diff --git a/contrib/nnc/passes/caffe_frontend/caffe_model_visitor.h b/contrib/nnc/passes/caffe_frontend/caffe_model_visitor.h
deleted file mode 100644 (file)
index 621c09c..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef NNCC_CAFFE_IR_VISITOR_H
-#define NNCC_CAFFE_IR_VISITOR_H
-
-#include <map>
-#include <vector>
-
-#include "core/modelIR/graph.h"
-#include "core/modelIR/ir_node.h"
-#include "core/modelIR/TensorVariant.h"
-
-#include "caffe_visitor.h"
-#include "caffe_op_creator.h"
-
-namespace nnc
-{
-namespace caffe
-{
-
-class ModelVisitor : public Visitor
-{
-public:
-    ModelVisitor() : graph(new mir::Graph()), opCreator(graph) {};
-
-    void visit(const ::caffe::NetParameter&) override;
-    void visit(const ::caffe::LayerParameter&) override;
-    void visit(const ::caffe::BlobProto&) override;
-    void visit(const ::caffe::BlobShape&) override;
-
-    mir::Graph* getGraph();
-    void setGraphOutputs();
-    void setIrNodeNames();
-
-private:
-    mir::Graph* graph = nullptr;
-    OpCreator opCreator;
-
-    std::vector<mir::Shape> inputShapes;
-    std::map<std::string, mir::INode::Ref> opsForBlobsTheyOutput;
-    std::vector<mir::INode::Ref> graphOutputs;
-
-    std::shared_ptr<mir::TensorVariant> createTensor(const ::caffe::BlobProto&);
-    std::vector<mir::INode::Ref> createOpInputs(const ::caffe::LayerParameter&);
-    std::vector<std::shared_ptr<mir::TensorVariant>> createOpParams(const ::caffe::LayerParameter&);
-
-    void createGraphInputs(const std::vector<std::string> &names,
-                           const std::vector<mir::Shape> &shapes);
-    void processInputLayer(const ::caffe::LayerParameter&);
-    void processDeprecatedInput(const ::caffe::NetParameter&);
-};
-
-} // namespace caffe
-} // namespace nnc
-
-#endif //NNCC_CAFFE_IR_VISITOR_H
index a5ee162..a549c07 100644 (file)
 #include "pass/PassException.h"
 #include "caffe_op_creator.h"
 
+#include <set>
 #include <cmath>
 
 
-namespace nnc
-{
-namespace caffe
-{
+namespace nnc {
 
 using namespace mir;
 
-
 template <typename OptsType>
-static inline bool has2DStride(const OptsType& opts)
-{
+static inline bool has2DStride(const OptsType& opts) {
   if (opts.has_stride_h() != opts.has_stride_w())
-  {
     throw PassException("Conv or Pool layer has only 1 out of 2 2D strides, investigate");
-  }
   // We already checked that both 2D strides are both present or both are not
   return opts.has_stride_h();
 }
 
-static inline Shape getStrideFromOneValue(bool hasStride, uint32_t stride)
-{
-  if (hasStride)
-  {
+static inline Shape getStrideFromOneValue(bool hasStride, uint32_t stride) {
+  if (hasStride) {
     return Shape{static_cast<int32_t>(stride), static_cast<int32_t>(stride), 1};
   }
   else
-  {
     return Shape{1, 1, 1};
-  }
 }
 
-static inline Shape getStrideFromTwoValues(uint32_t stride1, uint32_t stride2)
-{
+static inline Shape getStrideFromTwoValues(uint32_t stride1, uint32_t stride2) {
   return Shape{static_cast<int32_t>(stride1), static_cast<int32_t>(stride2), 1};
 }
 
 template <typename OptsType>
-static inline Shape getStride(const OptsType& opts)
-{
+static inline Shape getStride(const OptsType& opts) {
   // stride might have size 0, then it defaults to 1
   if (opts.stride_size() == 0)
-  {
     return Shape{1, 1, 1};
-  }
   else if (opts.stride_size() == 1)
-  {
     return getStrideFromOneValue(true, opts.stride(0));
-  }
   else
-  {
     return getStrideFromTwoValues(opts.stride(0), opts.stride(1));
-  }
 }
 
 /**
@@ -99,45 +81,35 @@ static inline Shape getStride(const OptsType& opts)
  * @todo Currently, stride_h and stride_w options take precedence if they are present,
  * but maybe it is not correct logic. Check how it really is done.
  */
-__attribute__ ((unused)) static Shape getConvStride(const ConvolutionParameter& opts)
-{
+__attribute__ ((unused)) static Shape getConvStride(const ConvolutionParameter& opts) {
   if (has2DStride(opts))
     return getStrideFromTwoValues(opts.stride_h(), opts.stride_w());
   else
     return getStride(opts);
 }
 
-__attribute__ ((unused)) static Shape getPoolStride(const PoolingParameter& opts)
-{
+__attribute__ ((unused)) static Shape getPoolStride(const PoolingParameter& opts) {
   if (has2DStride(opts))
     return getStrideFromTwoValues(opts.stride_h(), opts.stride_w());
   else
     return getStrideFromOneValue(opts.has_stride(), opts.stride());
 }
 
-__attribute__ ((unused)) static Shape getPoolWindowShape(const PoolingParameter &opts)
-{
+__attribute__ ((unused)) static Shape getPoolWindowShape(const PoolingParameter &opts) {
   if (opts.has_kernel_h() != opts.has_kernel_w())
-  {
     throw PassException("Pool layer has only 1 out of 2 kernel dimensions, investigate");
-  }
 
-  if (opts.has_kernel_h())
-  {
+  if (opts.has_kernel_h()) {
     return Shape{static_cast<int32_t>(opts.kernel_h()), static_cast<int32_t>(opts.kernel_w()), 1};
   }
-  else if (opts.has_kernel_size())
-  {
+  else if (opts.has_kernel_size()) {
     return Shape{static_cast<int32_t>(opts.kernel_size()), static_cast<int32_t>(opts.kernel_size()), 1};
   }
   else
-  {
     throw PassException("Pooling layer doesn't have kernel size data, investigate");
-  }
 }
 
-__attribute__ ((unused)) static ops::PoolOp::PoolingType getPoolingType(const PoolingParameter& opts)
-{
+__attribute__ ((unused)) static ops::PoolOp::PoolingType getPoolingType(const PoolingParameter& opts) {
   using PoolingType = ops::PoolOp::PoolingType;
 
   if (opts.pool() == PoolingParameter::MAX)
@@ -155,24 +127,18 @@ __attribute__ ((unused)) static ops::PoolOp::PoolingType getPoolingType(const Po
  * @todo Decide how to process axis in general.
  */
 template <typename OptsType>
-__attribute__ ((unused)) static int getAxisValue(const OptsType& opts)
-{
+__attribute__ ((unused)) static int getAxisValue(const OptsType& opts) {
   // -1 represents last one dimension
   int axis = -1;
-  if (opts.has_axis())
-  {
+  if (opts.has_axis()) {
     axis = opts.axis();
     if (axis == 0)
-    {
       std::cout << "WARNING: axis parameter equals 0. It is normal,"
                    "but implies that the model might not have a batch dimension,"
                    "so make sure import works correctly." << std::endl;
-    }
     else if (axis != 1 && axis != -1)
-    {
       throw PassException("Softmax/Concat layer axis param is not 1 or -1, which implies"
                             "unsupported NN architecture.");
-    }
   }
 
   // axis 1 represents channels in caffe, in Model ir it is second dimension for now
@@ -193,8 +159,7 @@ __attribute__ ((unused)) static int getAxisValue(const OptsType& opts)
  * @param foldedKernel original grouped kernel
  * @return unfolded kernel, compatible with ordinary conv2D operation
  */
-static std::shared_ptr<IrTensor> fixGroupedKernel(int groups, std::shared_ptr<IrTensor> foldedKernel)
-{
+static std::shared_ptr<IrTensor> fixGroupedKernel(int groups, std::shared_ptr<IrTensor> foldedKernel) {
   const int kernelInChanNum = 2;
   const int kernelOutChanNum = 3;
 
@@ -218,21 +183,18 @@ static std::shared_ptr<IrTensor> fixGroupedKernel(int groups, std::shared_ptr<Ir
   assert(kernelOutChannels % groups == 0);
 
   // Iterate over "unfolded" kernel Shape and insert appropriate values into result kernel
-  for (const mir::Index &idx: mir::ShapeRange(unfoldKernelShape))
-  {
+  for (const mir::Index &idx: mir::ShapeRange(unfoldKernelShape)) {
     auto inGroupNo = idx.at(kernelInChanNum) / inGroupSize;
     auto outGroupNo = idx.at(kernelOutChanNum) / outGroupSize;
     // check that input channel group fits output channel group
-    if (inGroupNo == outGroupNo)
-    {
+    if (inGroupNo == outGroupNo) {
       // compute index in original kernel that corresponds output index
       mir::Index foldedIdx(idx);
       foldedIdx.at(kernelInChanNum) %= inGroupSize;
 
       std::copy(foldedKernel->at(foldedIdx), foldedKernel->at(foldedIdx) + dataSize, unfoldKernel->at(idx));
     }
-    else
-    {
+    else {
       // fill element of output kernel with zero element
       assert(foldedKernel->getDataType() == IrTensor::DTYPE::FLOAT && "unsupported data type, add appropriate zero element creation");
       float *elem = reinterpret_cast<float *>(unfoldKernel->at(idx));
@@ -242,18 +204,24 @@ static std::shared_ptr<IrTensor> fixGroupedKernel(int groups, std::shared_ptr<Ir
   return unfoldKernel;
 }
 
-
-std::vector<INode::Ref> OpCreator::createConv2D(InputOps inputs, InputParams params,
-                                                const caffe::ConvolutionParameter& opts)
-{
+void OpCreator::checkConv2D(const caffe::ConvolutionParameter& opts,
+                            std::set<std::string> &problemsOpSet) {
   assert(opts.stride_size() <= 2);
 
+  if (opts.pad_size() != 0 && (opts.has_pad_h() || opts.has_pad_w()))
+    problemsOpSet.insert("Conv2D: Conflicting padding properties");
+
+  if (opts.pad_size() > 2)
+    problemsOpSet.insert("Conv2D: Unsupported number of pads");
+}
+
+std::vector<INode::Ref> OpCreator::createConv2D(InputOps inputs, InputParams params,
+                                                const caffe::ConvolutionParameter& opts) {
   ops::PaddingType padType = ops::PaddingType::Custom;
   Shape strideShape = getConvStride(opts);
 
   std::shared_ptr<IrTensor> unfoldedTensor = params[0];
-  if (opts.group() != 1)
-  {
+  if (opts.group() != 1) {
     // first we need to convert kernel of grouped convolution to appropriate ordinary kernel
     unfoldedTensor = fixGroupedKernel(opts.group(), params[0]);
   }
@@ -263,22 +231,11 @@ std::vector<INode::Ref> OpCreator::createConv2D(InputOps inputs, InputParams par
   // Set pads
   auto *op = static_cast<ops::Conv2DOp *>(outputs[0]->getOperation());
 
-  if (opts.pad_size() != 0 && (opts.has_pad_h() || opts.has_pad_w()))
-    throw PassException("Conflicting padding properties in convolution");
-
   int pad_h = opts.has_pad_h() ? opts.pad_h() : 0;
   int pad_w = opts.has_pad_w() ? opts.pad_w() : 0;
-  switch (opts.pad_size())
-  {
-  case 0:
-    // no common padding property set
-    break;
-  case 1:
+  if (opts.pad_size() == 1)
     pad_h = pad_w = opts.pad(0);
-    break;
-  default:
-    throw PassException("Unsupported number of pads");
-  }
+
   op->setPadding(0, pad_h);
   op->setPadding(1, pad_w);
   op->setPadding(2, 0);
@@ -290,6 +247,15 @@ std::vector<INode::Ref> OpCreator::createConv2D(InputOps inputs, InputParams par
     return outputs;
 }
 
+void OpCreator::checkFullyConnected(const caffe::InnerProductParameter &opts,
+                                    std::set<std::string> &problemsOpSet) {
+  if (opts.has_axis() && opts.axis() != 1)
+    problemsOpSet.insert("Fully Connected: layer axis param is not supported yet");
+
+  if (opts.has_transpose() && opts.transpose())
+    problemsOpSet.insert("Fully Connected: layer transpose param is not supported yet");
+}
+
 /**
  * @brief Converts Caffe InnerProduct layer to Model IR FullyConnected operation.
  * @todo InnerProduct layer take NCHW input and flattens the CHW part. We insert the
@@ -299,18 +265,7 @@ std::vector<INode::Ref> OpCreator::createConv2D(InputOps inputs, InputParams par
  * @todo Support axis and transpose parameters as needed.
  */
 std::vector<INode::Ref> OpCreator::createFullyConnected(InputOps &inputs, InputParams &params,
-                                                        const caffe::InnerProductParameter &opts)
-{
-  if (opts.has_axis() && opts.axis() != 1)
-  {
-    throw PassException("InnerProduct layer axis param is not supported yet");
-  }
-
-  if (opts.has_transpose() && opts.transpose())
-  {
-    throw PassException("InnerProduct layer transpose param is not supported yet");
-  }
-
+                                                        const caffe::InnerProductParameter &opts) {
   // Add Reshape operation to make sure the input for FC operation has shape [1, fcInputSize]
   // It is needed because Caffe InnerProduct layer takes NCHW input and flattens the CHW part.
   auto outputs = createOp<ops::ReshapeOp>(inputs);
@@ -328,30 +283,35 @@ std::vector<INode::Ref> OpCreator::createFullyConnected(InputOps &inputs, InputP
 }
 
 std::vector<INode::Ref> OpCreator::createConcat(InputOps inputs, InputParams params,
-                                                const caffe::ConcatParameter& opts)
-{
+                                                const caffe::ConcatParameter& opts) {
   (void)params;
 
   return createOp<ops::ConcatOp>(inputs, inputs.size(), getAxisValue(opts));
 }
 
+void OpCreator::checkPool(const caffe::PoolingParameter &opts,
+                          std::set<std::string> &problemsOpSet) {
+  if (opts.has_global_pooling() && opts.global_pooling())
+    problemsOpSet.insert("Pooling: pooling layer global_pooling param is not supported yet");
+
+  ops::PoolOp::PoolingType poolType = getPoolingType(opts);
+  if (poolType != ops::PoolOp::PoolingType::AVG && poolType != ops::PoolOp::PoolingType::MAX)
+    problemsOpSet.insert("Pooling: unsupported pooling type");
+
+  if (opts.has_pad() && (opts.has_pad_h() || opts.has_pad_w()))
+    problemsOpSet.insert("Pooling: conflicting padding properties in pooling");
+}
+
 std::vector<INode::Ref> OpCreator::createPool(InputOps inputs, InputParams params,
-                                              const caffe::PoolingParameter& opts)
-{
+                                              const caffe::PoolingParameter& opts) {
   (void)params;
 
-  if (opts.has_global_pooling() && opts.global_pooling())
-  {
-    throw PassException("Pooling layer global_pooling param is not supported yet");
-  }
-
   Shape windowShape = getPoolWindowShape(opts);
   ops::PoolOp::PoolingType poolType = getPoolingType(opts);
   ops::PaddingType padType = ops::PaddingType::Custom;
   Shape stride = getPoolStride(opts);
   ops::PoolOp::BorderType borderType;
-  switch (poolType)
-  {
+  switch (poolType) {
     case ops::PoolOp::PoolingType::AVG:
       borderType = ops::PoolOp::BorderType::ZEROFILLED;
       break;
@@ -359,22 +319,18 @@ std::vector<INode::Ref> OpCreator::createPool(InputOps inputs, InputParams param
       borderType = ops::PoolOp::BorderType::EMPTY;
       break;
     default:
-      throw PassException("Unsupported pooling type");
+      // This check performed in checkPool()
+      assert(false);
   }
 
   auto pooling = createOp<ops::PoolOp>(inputs, windowShape, stride, poolType, padType, borderType);
 
   // Set pads
   auto op = static_cast<ops::PoolOp *>(pooling[0]->getOperation());
-  if (opts.has_pad() && (opts.has_pad_h() || opts.has_pad_w()))
-    throw PassException("Conflicting padding properties in pooling");
-
   int pad_h = opts.has_pad_h() ? opts.pad_h() : 0;
   int pad_w = opts.has_pad_w() ? opts.pad_w() : 0;
   if (opts.has_pad())
-  {
     pad_h = pad_w = opts.pad();
-  }
   op->setPadding(0, pad_h);
   op->setPadding(1, pad_w);
   op->setPadding(2, 0);
@@ -383,13 +339,27 @@ std::vector<INode::Ref> OpCreator::createPool(InputOps inputs, InputParams param
 }
 
 std::vector<INode::Ref> OpCreator::createSoftmax(InputOps inputs, InputParams params,
-                                                 const caffe::SoftmaxParameter& opts)
-{
+                                                 const caffe::SoftmaxParameter& opts) {
   (void)params;
 
   return createOp<ops::SoftmaxOp>(inputs, getAxisValue(opts));
 }
 
+void OpCreator::checkReshape(const caffe::ReshapeParameter &opts,
+                             std::set<std::string> &problemsOpSet) {
+  if (opts.has_axis() || opts.has_num_axes())
+    problemsOpSet.insert("Reshape layer axis and num_axes params are not supported yet");
+
+  if (!opts.has_shape())
+    problemsOpSet.insert("Reshape layer doesn't have shape parameter");
+
+  Shape newShape = ShapeHelper::createShape(opts.shape().dim(), opts.shape().dim_size());
+
+  for (int32_t i = 0; i < newShape.rank(); ++i)
+    if (newShape.dim(i) == 0)
+      problemsOpSet.insert("Reshape layer zero shape values are not supported yet");
+}
+
 /**
  * @brief Converts Caffe Reshape layer to Model IR Reshape operation.
  * @todo Support "axis" and "num_axes" parameters as needed.
@@ -397,49 +367,31 @@ std::vector<INode::Ref> OpCreator::createSoftmax(InputOps inputs, InputParams pa
  * @todo Support zero values in "shape" parameter.
  */
 std::vector<INode::Ref> OpCreator::createReshape(InputOps inputs, InputParams params,
-                                                 const caffe::ReshapeParameter& opts)
-{
+                                                 const caffe::ReshapeParameter& opts) {
   (void)params;
 
   auto outputs = createOp<ops::ReshapeOp>(inputs);
 
-  if (opts.has_axis() || opts.has_num_axes())
-  {
-    throw PassException("Reshape layer axis and num_axes params are not supported yet");
-  }
-
-  if (!opts.has_shape())
-  {
-    throw PassException("Reshape layer doesn't have shape parameter");
-  }
-
   Shape newShape = ShapeHelper::createShape(opts.shape().dim(), opts.shape().dim_size());
 
-  for (int32_t i = 0; i < newShape.rank(); ++i)
-  {
-    if (newShape.dim(i) == 0)
-      throw PassException("Reshape layer zero shape values are not supported yet");
-  }
-
   outputs[0]->getOperation()->setOutputShape(0, newShape);
   return outputs;
 }
 
+void OpCreator::checkRelu(const caffe::ReLUParameter &opts,
+                          std::set<std::string> &problemsOpSet) {
+  if (opts.has_negative_slope())
+    problemsOpSet.insert("ReLU layer negative_slope param is not supported yet.");
+}
+
 std::vector<INode::Ref> OpCreator::createRelu(InputOps inputs, InputParams params,
-                                              const caffe::ReLUParameter& opts)
-{
+                                              const caffe::ReLUParameter& opts) {
   (void)params;
 
-  if (opts.has_negative_slope())
-  {
-    throw PassException("ReLU layer negative_slope param is not supported yet.");
-  }
-
   return createOp<ops::ReluOp>(inputs);
 }
 
-std::vector<INode::Ref> OpCreator::createScale(InputOps inputs, InputParams params, const ScaleParameter& opts)
-{
+std::vector<INode::Ref> OpCreator::createScale(InputOps inputs, InputParams params, const ScaleParameter& opts) {
   auto outputs = createOp<ops::ScaleOp>(inputs, std::move(*params[0]));
   // bias_term is optional (so might not be present) and defaults to true
   if (!opts.has_bias_term() || opts.bias_term())
@@ -448,8 +400,14 @@ std::vector<INode::Ref> OpCreator::createScale(InputOps inputs, InputParams para
     return outputs;
 }
 
-std::vector<INode::Ref> OpCreator::createBatchNorm(InputOps inputs, InputParams params, const BatchNormParameter& opts)
-{
+void OpCreator::checkBatchNorm(const caffe::BatchNormParameter &opts, InputParams params,
+                               std::set<std::string> &problemsOpSet) {
+  // Check that last blob(with scaleFactor) containing only one number
+  if (params[2]->getShape().rank() != 1 && params[2]->getShape().dim(0) != 1)
+    problemsOpSet.insert("Unexpected shape of scale parameter in batch norm");
+}
+
+std::vector<INode::Ref> OpCreator::createBatchNorm(InputOps inputs, InputParams params, const BatchNormParameter& opts) {
   const float MAFRAC_DEF =  0.999f;
   const float EPS_DEF =  1e-5f;
   // optional params may be left out, so we fill them with defalt values (lifted from caffe docs)
@@ -457,10 +415,6 @@ std::vector<INode::Ref> OpCreator::createBatchNorm(InputOps inputs, InputParams
   (void)moving_average_fraction;
   float eps = (opts.has_eps()) ? opts.eps() : EPS_DEF;
 
-  // Check that last blob(with scaleFactor) containing only one number
-  if (params[2]->getShape().rank() != 1 && params[2]->getShape().dim(0) != 1)
-    throw PassException("Unexpected shape of scale parameter in batch norm");
-
   float scaleFactor = *reinterpret_cast<float *>(params[2]->at(mir::Index{0}));
   // Code below is taken from cpu caffe implementation:
   // https://github.com/BVLC/caffe/blob/master/src/caffe/layers/batch_norm_layer.cpp#L100
@@ -473,9 +427,7 @@ std::vector<INode::Ref> OpCreator::createBatchNorm(InputOps inputs, InputParams
   Tensor<float> biasData(*params[0]);
 
   for (Index idx: ShapeRange(biasData.getShape()))
-  {
     biasData.at(idx) *= -scaleFactor;
-  }
   auto meanOutputs = createOp<ops::BiasAddOp>(inputs, std::move(*params[0]));
 
   // create scale argument from variance:
@@ -483,16 +435,13 @@ std::vector<INode::Ref> OpCreator::createBatchNorm(InputOps inputs, InputParams
   // normalize biased input using scale operation
   Tensor<float> scaleData(*params[1]);
   for (Index idx: ShapeRange(scaleData.getShape()))
-  {
     scaleData.at(idx) = 1.0f/std::sqrt(scaleData.at(idx)*scaleFactor + eps);
-  }
   auto varianceOutputs = createOp<ops::ScaleOp>(meanOutputs, std::move(*params[1]));
 
   return varianceOutputs;
 }
 
-std::vector<INode::Ref> OpCreator::createDropout(InputOps inputs, InputParams params, const DropoutParameter& opts)
-{
+std::vector<INode::Ref> OpCreator::createDropout(InputOps inputs, InputParams params, const DropoutParameter& opts) {
   (void)params;
   const float DROPOUT_RATIO_DEF = 0.5f;
   // optional params may be left out, so we fill them with defalt values (lifted from caffe docs)
@@ -500,15 +449,11 @@ std::vector<INode::Ref> OpCreator::createDropout(InputOps inputs, InputParams pa
   return createOp<ops::DropoutOp>(inputs, dropot_ratio);
 }
 
-void OpCreator::connectInputs(INode::Ref op, InputOps inputs)
-{
+void OpCreator::connectInputs(INode::Ref op, InputOps inputs) {
   // TODO: this part doesn't support the situation where an operator takes as input
   // some tensor that is not the 0th output of some other operator
   for (int i = 0; i < static_cast<int>(inputs.size()); ++i)
-  {
     op->connectInputTo(i, inputs[i]->getOutput(0));
-  }
 }
 
-} // namespace caffe
 } // namespace nnc
index a901ce0..e7b8bfe 100644 (file)
@@ -17,6 +17,7 @@
 #ifndef NNCC_CAFFE_OP_CREATOR_H
 #define NNCC_CAFFE_OP_CREATOR_H
 
+#include <set>
 #include <map>
 #include <vector>
 #include <memory>
 
 #include "caffe/proto/caffe.pb.h"
 
-namespace nnc
-{
-namespace caffe
-{
+namespace nnc {
 
 using namespace ::caffe;
 
-namespace ops = nnc::mir::ops;
 using nnc::mir::Graph;
 using nnc::mir::INode;
 using IrTensor = nnc::mir::TensorVariant;
 using nnc::mir::Shape;
 
-class OpCreator
-{
+class OpCreator {
 public:
     using InputOps = std::vector<INode::Ref>&;
     using InputParams = std::vector<std::shared_ptr<IrTensor>>&;
@@ -61,6 +57,14 @@ public:
     std::vector<INode::Ref> createBatchNorm(InputOps inputs, InputParams params, const BatchNormParameter& opts);
     std::vector<INode::Ref> createDropout(InputOps inputs, InputParams params, const DropoutParameter& opts);
 
+    static void checkConv2D(const caffe::ConvolutionParameter& opts, std::set<std::string> &unsupportedOpSet);
+    static void checkFullyConnected(const caffe::InnerProductParameter &opts, std::set<std::string> &problemsOpSet);
+    static void checkPool(const caffe::PoolingParameter &opts, std::set<std::string> &problemsOpSet);
+    static void checkReshape(const caffe::ReshapeParameter &opts, std::set<std::string> &problemsOpSet);
+    static void checkRelu(const caffe::ReLUParameter &opts, std::set<std::string> &problemsOpSet);
+    static void checkBatchNorm(const caffe::BatchNormParameter &opts, InputParams params,
+                               std::set<std::string> &problemsOpSet);
+
 private:
     Graph* graph = nullptr;
 
@@ -71,8 +75,7 @@ private:
 };
 
 template<typename OpType, typename ...Types>
-std::vector<INode::Ref> OpCreator::createOp(std::vector<INode::Ref>& inputs, Types&&... args)
-{
+std::vector<INode::Ref> OpCreator::createOp(std::vector<INode::Ref>& inputs, Types&&... args) {
     std::vector<INode::Ref> outputs;
 
     // TODO: set operation names
@@ -84,7 +87,6 @@ std::vector<INode::Ref> OpCreator::createOp(std::vector<INode::Ref>& inputs, Typ
     return outputs;
 }
 
-} // namespace caffe
 } // namespace nnc
 
 #endif //NNCC_CAFFE_OP_CREATOR_H
diff --git a/contrib/nnc/passes/caffe_frontend/caffe_op_types.h b/contrib/nnc/passes/caffe_frontend/caffe_op_types.h
new file mode 100644 (file)
index 0000000..e95b28a
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef NNCC_CAFFE_OP_TYPES_H
+#define NNCC_CAFFE_OP_TYPES_H
+
+enum class CaffeOpType {
+  absVal,
+  accuracy,
+  argMax,
+  batchNorm,
+  batchReindex,
+  bias,
+  BNLL,
+  clip,
+  concat,
+  contrastiveLoss,
+  convolution,
+  crop,
+  data,
+  deconvolution,
+  dropout,
+  dummyData,
+  eltwise,
+  ELU,
+  embed,
+  euclidianLoss,
+  exp,
+  filter,
+  flatten,
+  HDF5Data,
+  HDF5Output,
+  hingeLoss,
+  im2Col,
+  imageData,
+  infogainLoss,
+  innerProduct,
+  input,
+  log,
+  LRN,
+  LSTM,
+  memoryData,
+  multinomialLogisticLoss,
+  MVN,
+  parameter,
+  pooling,
+  power,
+  PReLU,
+  python,
+  recurrent,
+  reduction,
+  ReLU,
+  reshape,
+  RNN,
+  scale,
+  sigmoidCrossEntropyLoss,
+  sigmoid,
+  silence,
+  slice,
+  softmax,
+  softmaxLoss,
+  split,
+  SPP,
+  tanh,
+  threshold,
+  tile,
+  windowData
+};
+
+#endif //NNCC_CAFFE_OP_TYPES_H
diff --git a/contrib/nnc/passes/caffe_frontend/caffe_visitor.h b/contrib/nnc/passes/caffe_frontend/caffe_visitor.h
deleted file mode 100644 (file)
index ec01e73..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef NNCC_CAFFE_VISITOR_H
-#define NNCC_CAFFE_VISITOR_H
-
-#include "caffe/proto/caffe.pb.h"
-
-namespace nnc
-{
-namespace caffe
-{
-
-using namespace ::caffe;
-
-class Visitor
-{
-public:
-    virtual ~Visitor() = default;
-
-    virtual void visit(const NetParameter&) = 0;
-    virtual void visit(const LayerParameter&) = 0;
-    virtual void visit(const BlobProto&) = 0;
-    virtual void visit(const BlobShape&) = 0;
-};
-
-} // namespace caffe
-} // namespace nnc
-
-#endif //NNCC_CAFFE_VISITOR_H
diff --git a/contrib/nnc/passes/caffe_frontend/caffe_walker.cpp b/contrib/nnc/passes/caffe_frontend/caffe_walker.cpp
deleted file mode 100644 (file)
index 4247aa5..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "caffe_walker.h"
-
-namespace nnc
-{
-namespace caffe
-{
-
-void ModelWalker::walkNetParameter(const NetParameter& np)
-{
-    visitor->visit(np);
-
-    for (int i = 0; i < np.layer_size(); ++i)
-    {
-        walkLayerParameter(np.layer(i));
-    }
-
-    for (int i = 0; i < np.input_shape_size(); ++i)
-    {
-        visitor->visit(np.input_shape(i));
-    }
-}
-
-void ModelWalker::walkLayerParameter(const LayerParameter& lp)
-{
-    visitor->visit(lp);
-
-    for (int i = 0; i < lp.blobs_size(); ++i)
-    {
-        visitor->visit(lp.blobs(i));
-    }
-}
-
-} // namespace caffe
-} // namespace nnc
diff --git a/contrib/nnc/passes/caffe_frontend/caffe_walker.h b/contrib/nnc/passes/caffe_frontend/caffe_walker.h
deleted file mode 100644 (file)
index 83a02f9..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef NNCC_CAFFE_WALKER_H
-#define NNCC_CAFFE_WALKER_H
-
-#include "caffe/proto/caffe.pb.h"
-
-#include "caffe_visitor.h"
-
-namespace nnc
-{
-namespace caffe
-{
-
-using namespace ::caffe;
-
-class ModelWalker
-{
-public:
-    ModelWalker(Visitor* visitor) : visitor(visitor) {};
-
-    void walkNetParameter(const NetParameter&);
-    void walkLayerParameter(const LayerParameter&);
-
-private:
-    Visitor* visitor;
-};
-
-} // namespace caffe
-} // namespace nnc
-
-#endif // NNCC_CAFFE_WALKER_H
index 6cba9ed..8718401 100644 (file)
 
 #include "proto_reader.h"
 
-namespace nnc
-{
-namespace caffe
-{
+namespace nnc {
 
 const int protoBytesLimit = INT_MAX;
 const int protoBytesWarningLimit = 1024 * 1024 * 512;
 
-bool readProtoFromTextFile(const char* filename, ::caffe::NetParameter* proto)
-{
+bool readProtoFromTextFile(const char* filename, ::caffe::NetParameter* proto) {
     int fd = open(filename, O_RDONLY);
-    if (fd == -1)
-    {
+    if (fd == -1) {
         std::cout << "File not found: " << filename << std::endl;
         return false;
     }
@@ -49,11 +44,9 @@ bool readProtoFromTextFile(const char* filename, ::caffe::NetParameter* proto)
     return success;
 }
 
-bool readProtoFromBinaryFile(const char* filename, ::caffe::NetParameter* proto)
-{
+bool readProtoFromBinaryFile(const char* filename, ::caffe::NetParameter* proto) {
     int fd = open(filename, O_RDONLY);
-    if (fd == -1)
-    {
+    if (fd == -1) {
         std::cout << "File not found: " << filename << std::endl;
         return false;
     }
@@ -69,5 +62,4 @@ bool readProtoFromBinaryFile(const char* filename, ::caffe::NetParameter* proto)
     return success;
 }
 
-} // namespace caffe
 } // namespace nnc
index 16aaf4b..c3810bb 100644 (file)
 #include "google/protobuf/io/zero_copy_stream_impl.h"
 #include "google/protobuf/text_format.h"
 
-namespace nnc
-{
-namespace caffe
-{
+namespace nnc {
 
 using google::protobuf::io::FileInputStream;
 using google::protobuf::io::ZeroCopyInputStream;
@@ -33,7 +30,6 @@ using google::protobuf::io::CodedInputStream;
 bool readProtoFromTextFile(const char* filename, ::caffe::NetParameter* proto);
 bool readProtoFromBinaryFile(const char* filename, ::caffe::NetParameter* proto);
 
-} // namespace caffe
 } // namespace nnc
 
 #endif // NNCC_PROTO_READER_H
index 69655d1..3f2e96f 100644 (file)
@@ -41,12 +41,7 @@ PassData TFLiteFrontend::run(PassData data)
 {
   nnc::tflite::v3::TfliteImporter importer{cli::inputFile};
 
-  bool success = importer.import();
-
-  if (!success)
-  {
-    throw PassException("Could not load model: " + cli::inputFile + "\n");
-  }
+  importer.import();
 
   return reinterpret_cast<mir::Graph *>(importer.createIR());
 }
index b2838e3..27b6dbb 100644 (file)
 
 TfliteImporter::TfliteImporter(std::string filename)
 {
+  _modelFilename = filename;
   modelRaw.reset(new ModelAllocation{std::move(filename)});
 }
 
-bool TfliteImporter::importUnpacked()
+void TfliteImporter::importUnpacked()
 {
-  bool importSuccess = import();
+  import();
 
-  if (importSuccess)
-  {
-    model.reset(modelPacked->UnPack());
-  }
-
-  return importSuccess;
+  model.reset(modelPacked->UnPack());
 }
 
-bool TfliteImporter::import()
+void TfliteImporter::import()
 {
   const void *modelBuffer = modelRaw->getDataPnt();
 
   if (!modelBuffer)
-  {
-    return false;
-  }
+    throw PassException("Could not load model: " + _modelFilename+ "\n");
 
   auto verifier = flatbuffers::Verifier(reinterpret_cast<const uint8_t *>(modelBuffer),
                                         modelRaw->getNumBytes());
 
-  bool importSuccess = VerifyModelBuffer(verifier);
-  if (importSuccess)
-  {
-    modelPacked = GetModel(modelRaw->getDataPnt());
-  }
+  if (!VerifyModelBuffer(verifier))
+    throw PassException("Could not load model: " + _modelFilename + "\n");
 
-  return importSuccess;
+  modelPacked = GetModel(modelRaw->getDataPnt());
 }
 
-void *TfliteImporter::createIR()
+Graph *TfliteImporter::createIR()
 {
   IrVisitor irCreator{};
   ModelWalker walker{std::vector<Visitor *>{&irCreator}};
@@ -64,11 +55,3 @@ void *TfliteImporter::createIR()
   return irCreator.getGraph();
 }
 
-void TfliteImporter::dump()
-{
-  DumpVisitor dumper{};
-  ModelWalker walker{std::vector<Visitor *>{&dumper}};
-
-  walker.walk(modelPacked);
-}
-
index 7ddffde..6bb1191 100644 (file)
  * limitations under the License.
  */
 
-
-class TfliteImporter : NNImporter
-{
+class TfliteImporter : NNImporter {
 public:
   explicit TfliteImporter(std::string filename);
 
-  bool import() override;
-  void *createIR() override;
-  void dump() override;
+  void import() override;
+  mir::Graph *createIR() override;
 
-  bool importUnpacked();
+  void importUnpacked();
 
 protected:
+  std::string _modelFilename;
   std::unique_ptr<ModelAllocation> modelRaw;
   std::unique_ptr<ModelT> model;
   const Model *modelPacked = nullptr;
index 2a4236b..fe49fa2 100644 (file)
@@ -23,6 +23,7 @@
 #include "schema_v3.h"
 #include "passes/common_frontend/nn_importer.h"
 #include "passes/common_frontend/model_allocation.h"
+#include "pass/PassException.h"
 
 namespace nnc
 {
index cb40ea5..9b3d338 100644 (file)
 
 using namespace nnc;
 
-int main(int argc, const char **argv)
-{
+int main(int argc, const char **argv) {
   if (argc != 2)
-  {
     return 1;
-  }
 
   cli::CommandLine::getParser()->parseCommandLine(argc, argv);
   std::string modelName = cli::inputFile;
 
-  nnc::caffe::CaffeImporter importer{modelName};
-
-  bool success = importer.import();
+  nnc::CaffeImporter importer{modelName};
 
-  if (!success)
-  {
-    std::cout << "Could not load model \"" << modelName << "\"" << std::endl;
-    return 1;
-  }
+  importer.import();
 
-  try
-  {
+  try {
     importer.createIR();
   }
-  catch (...)
-  {
+  catch (...) {
     std::cout << "Could not create IR for model \"" << modelName << "\"" << std::endl;
     return 1;
   }
index 8ed4ee9..8d6e78c 100644 (file)
@@ -34,13 +34,7 @@ int main(int argc, const char **argv)
 
   nnc::tflite::v3::TfliteImporter importer{modelName};
 
-  bool success = importer.import();
-
-  if (!success)
-  {
-    std::cout << "Could not load model \"" << modelName << "\"" << std::endl;
-    return 1;
-  }
+  importer.import();
 
   try
   {
index 8093cbc..d587200 100644 (file)
@@ -16,3 +16,4 @@ add_subdirectory(pass)
 add_subdirectory(core)
 add_subdirectory(soft_backend)
 add_subdirectory(support)
+add_subdirectory(caffe_frontend)
diff --git a/contrib/nnc/unittests/caffe_frontend/CMakeLists.txt b/contrib/nnc/unittests/caffe_frontend/CMakeLists.txt
new file mode 100644 (file)
index 0000000..57484b7
--- /dev/null
@@ -0,0 +1,9 @@
+file(GLOB_RECURSE TESTS "*.cpp")
+
+add_definitions(-DTEST_DIR="${CMAKE_CURRENT_SOURCE_DIR}/test_data/")
+
+add_nnc_unit_test(nnc_caffe_frontend_test ${TESTS} ${OPTIONS_SRC})
+if (TARGET nnc_caffe_frontend_test)
+    nncc_target_link_libraries(nnc_caffe_frontend_test caffe_importer nnc_support nnc_core)
+    target_include_directories(nnc_caffe_frontend_test PRIVATE ${NNC_CAFFE_FRONTEND_DIR})
+endif()
diff --git a/contrib/nnc/unittests/caffe_frontend/test_data/unsupported.caffemodel b/contrib/nnc/unittests/caffe_frontend/test_data/unsupported.caffemodel
new file mode 100644 (file)
index 0000000..eab7397
Binary files /dev/null and b/contrib/nnc/unittests/caffe_frontend/test_data/unsupported.caffemodel differ
diff --git a/contrib/nnc/unittests/caffe_frontend/unsupportedCaffeModel.cpp b/contrib/nnc/unittests/caffe_frontend/unsupportedCaffeModel.cpp
new file mode 100644 (file)
index 0000000..c1a70e0
--- /dev/null
@@ -0,0 +1,27 @@
+#include "caffe_importer.h"
+#include "gtest/gtest.h"
+#include "pass/PassException.h"
+#include <string>
+#include <iostream>
+
+const char *ErrorMsg = "Detected problems:\n"
+                       "DummyData: unsupported layer\n"
+                       "LSTM: unsupported layer\n"
+                       "Sasha: unknown layer\n";
+
+// When adding support for new layers, change the model, not the test
+TEST(CAFFE_IMPORT_UNSUPPORTED, ImportAModelWithUnsupportedLayers) {
+  std::string filename = std::string(TEST_DIR) + "unsupported.caffemodel";
+
+  nnc::CaffeImporter importer{filename};
+  try {
+    importer.import();
+    importer.createIR();
+  }
+  catch (nnc::PassException &e) {
+    ASSERT_EQ(ErrorMsg, e.reason());
+    return;
+  }
+
+  FAIL();
+}