summaryrefslogtreecommitdiff
path: root/compiler/mir-interpreter/src
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/mir-interpreter/src')
-rw-r--r--compiler/mir-interpreter/src/MirInterpreter.cpp420
-rw-r--r--compiler/mir-interpreter/src/ops/Abs.cpp55
-rw-r--r--compiler/mir-interpreter/src/ops/Abs.h29
-rw-r--r--compiler/mir-interpreter/src/ops/Add.cpp130
-rw-r--r--compiler/mir-interpreter/src/ops/Add.h29
-rw-r--r--compiler/mir-interpreter/src/ops/AvgPool2D.cpp173
-rw-r--r--compiler/mir-interpreter/src/ops/AvgPool2D.h31
-rw-r--r--compiler/mir-interpreter/src/ops/CappedReLU.cpp82
-rw-r--r--compiler/mir-interpreter/src/ops/CappedReLU.h29
-rw-r--r--compiler/mir-interpreter/src/ops/Common.cpp37
-rw-r--r--compiler/mir-interpreter/src/ops/Common.h65
-rw-r--r--compiler/mir-interpreter/src/ops/Concat.cpp172
-rw-r--r--compiler/mir-interpreter/src/ops/Concat.h30
-rw-r--r--compiler/mir-interpreter/src/ops/Conv2D.cpp261
-rw-r--r--compiler/mir-interpreter/src/ops/Conv2D.h32
-rw-r--r--compiler/mir-interpreter/src/ops/DeConv2D.cpp122
-rw-r--r--compiler/mir-interpreter/src/ops/DeConv2D.h41
-rw-r--r--compiler/mir-interpreter/src/ops/DepthwiseConv2D.cpp225
-rw-r--r--compiler/mir-interpreter/src/ops/DepthwiseConv2D.h32
-rw-r--r--compiler/mir-interpreter/src/ops/Div.cpp62
-rw-r--r--compiler/mir-interpreter/src/ops/Div.h29
-rw-r--r--compiler/mir-interpreter/src/ops/ELU.cpp51
-rw-r--r--compiler/mir-interpreter/src/ops/ELU.h29
-rw-r--r--compiler/mir-interpreter/src/ops/Equal.cpp58
-rw-r--r--compiler/mir-interpreter/src/ops/Equal.h29
-rw-r--r--compiler/mir-interpreter/src/ops/Fill.h48
-rw-r--r--compiler/mir-interpreter/src/ops/FullyConnected.cpp214
-rw-r--r--compiler/mir-interpreter/src/ops/FullyConnected.h32
-rw-r--r--compiler/mir-interpreter/src/ops/Gather.cpp92
-rw-r--r--compiler/mir-interpreter/src/ops/Gather.h31
-rw-r--r--compiler/mir-interpreter/src/ops/Greater.cpp57
-rw-r--r--compiler/mir-interpreter/src/ops/Greater.h29
-rw-r--r--compiler/mir-interpreter/src/ops/HardSwish.cpp54
-rw-r--r--compiler/mir-interpreter/src/ops/HardSwish.h29
-rw-r--r--compiler/mir-interpreter/src/ops/LeakyReLU.cpp49
-rw-r--r--compiler/mir-interpreter/src/ops/LeakyReLU.h29
-rw-r--r--compiler/mir-interpreter/src/ops/Less.cpp57
-rw-r--r--compiler/mir-interpreter/src/ops/Less.h29
-rw-r--r--compiler/mir-interpreter/src/ops/Max.cpp66
-rw-r--r--compiler/mir-interpreter/src/ops/Max.h29
-rw-r--r--compiler/mir-interpreter/src/ops/MaxPool2D.cpp157
-rw-r--r--compiler/mir-interpreter/src/ops/MaxPool2D.h31
-rw-r--r--compiler/mir-interpreter/src/ops/Mul.cpp61
-rw-r--r--compiler/mir-interpreter/src/ops/Mul.h29
-rw-r--r--compiler/mir-interpreter/src/ops/Pad.cpp84
-rw-r--r--compiler/mir-interpreter/src/ops/Pad.h36
-rw-r--r--compiler/mir-interpreter/src/ops/Quantization.cpp71
-rw-r--r--compiler/mir-interpreter/src/ops/Quantization.h32
-rw-r--r--compiler/mir-interpreter/src/ops/QuantizationHelpers.h126
-rw-r--r--compiler/mir-interpreter/src/ops/ReLU.cpp58
-rw-r--r--compiler/mir-interpreter/src/ops/ReLU.h29
-rw-r--r--compiler/mir-interpreter/src/ops/ReduceMean.cpp98
-rw-r--r--compiler/mir-interpreter/src/ops/ReduceMean.h30
-rw-r--r--compiler/mir-interpreter/src/ops/Reshape.cpp38
-rw-r--r--compiler/mir-interpreter/src/ops/Reshape.h29
-rw-r--r--compiler/mir-interpreter/src/ops/Sigmoid.cpp58
-rw-r--r--compiler/mir-interpreter/src/ops/Sigmoid.h29
-rw-r--r--compiler/mir-interpreter/src/ops/Slice.cpp52
-rw-r--r--compiler/mir-interpreter/src/ops/Slice.h29
-rw-r--r--compiler/mir-interpreter/src/ops/Softmax.cpp155
-rw-r--r--compiler/mir-interpreter/src/ops/Softmax.h29
-rw-r--r--compiler/mir-interpreter/src/ops/Sqrt.cpp58
-rw-r--r--compiler/mir-interpreter/src/ops/Sqrt.h29
-rw-r--r--compiler/mir-interpreter/src/ops/Sub.cpp61
-rw-r--r--compiler/mir-interpreter/src/ops/Sub.h29
-rw-r--r--compiler/mir-interpreter/src/ops/Tanh.cpp58
-rw-r--r--compiler/mir-interpreter/src/ops/Tanh.h29
-rw-r--r--compiler/mir-interpreter/src/ops/Transpose.cpp64
-rw-r--r--compiler/mir-interpreter/src/ops/Transpose.h31
69 files changed, 4718 insertions, 0 deletions
diff --git a/compiler/mir-interpreter/src/MirInterpreter.cpp b/compiler/mir-interpreter/src/MirInterpreter.cpp
new file mode 100644
index 000000000..245f7ddab
--- /dev/null
+++ b/compiler/mir-interpreter/src/MirInterpreter.cpp
@@ -0,0 +1,420 @@
+/*
+ * 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 "MirInterpreter.h"
+
+#include "ops/Add.h"
+#include "ops/Abs.h"
+#include "ops/AvgPool2D.h"
+#include "ops/CappedReLU.h"
+#include "ops/Concat.h"
+#include "ops/Conv2D.h"
+#include "ops/DeConv2D.h"
+#include "ops/DepthwiseConv2D.h"
+#include "ops/Div.h"
+#include "ops/ELU.h"
+#include "ops/Equal.h"
+#include "ops/Fill.h"
+#include "ops/FullyConnected.h"
+#include "ops/Gather.h"
+#include "ops/Greater.h"
+#include "ops/HardSwish.h"
+#include "ops/LeakyReLU.h"
+#include "ops/Less.h"
+#include "ops/Max.h"
+#include "ops/MaxPool2D.h"
+#include "ops/Mul.h"
+#include "ops/Pad.h"
+#include "ops/Quantization.h"
+#include "ops/ReduceMean.h"
+#include "ops/ReLU.h"
+#include "ops/Reshape.h"
+#include "ops/Sigmoid.h"
+#include "ops/Slice.h"
+#include "ops/Softmax.h"
+#include "ops/Sqrt.h"
+#include "ops/Sub.h"
+#include "ops/Tanh.h"
+#include "ops/Transpose.h"
+
+#include "ops/Common.h"
+
+#include "mir/OpDefs.h"
+
+#include <cassert>
+#include <stdexcept>
+#include <string>
+#include <vector>
+
+namespace mir_interpreter
+{
+
+using namespace mir;
+
+void MIRInterpreter::setTensor(const Operation::Output *output, TensorVariant tensor)
+{
+ const auto result = _tensors.emplace(output, std::move(tensor));
+ if (!result.second)
+ {
+ const std::string &name = output->getName();
+ throw std::runtime_error("Attempt to overwrite data for tensor \"" + name + "\".");
+ }
+}
+
+TensorVariant &MIRInterpreter::allocateTensor(const Operation::Output *output)
+{
+ const auto result = _tensors.emplace(output, output->getType());
+ if (!result.second)
+ {
+ const std::string &name = output->getName();
+ throw std::runtime_error("Attempt to overwrite data for tensor \"" + name + "\".");
+ }
+ return result.first->second;
+}
+
+const TensorVariant &MIRInterpreter::getTensor(const Operation::Output *output) const
+{
+ const auto it = _tensors.find(output);
+ if (it == _tensors.end())
+ {
+ const std::string &name = output->getName();
+ throw std::runtime_error("Can't find data for tensor \"" + name + "\".");
+ }
+ return it->second;
+}
+
+std::vector<std::reference_wrapper<const TensorVariant>>
+MIRInterpreter::getInputTensors(const Operation &op)
+{
+ std::vector<std::reference_wrapper<const TensorVariant>> tensors;
+ for (const Operation::Output *input : op.getInputs())
+ {
+ tensors.emplace_back(getTensor(input));
+ }
+ return tensors;
+}
+
+std::vector<std::reference_wrapper<TensorVariant>>
+MIRInterpreter::allocateOutputTensors(const Operation &op)
+{
+ std::vector<std::reference_wrapper<TensorVariant>> tensors;
+ for (const Operation::Output &output : op.getOutputs())
+ {
+ tensors.emplace_back(allocateTensor(&output));
+ }
+ return tensors;
+}
+
+void MIRInterpreter::visit(ops::InputOp &op)
+{
+ assert(_tensors.find(op.getOutput(0)) != _tensors.end());
+}
+
+void MIRInterpreter::visit(ops::AvgPool2DOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto outputs = allocateOutputTensors(op);
+ AvgPool2D(op, inputs[0], outputs[0]);
+}
+
+void MIRInterpreter::visit(ops::ConstantOp &op) { setTensor(op.getOutput(0), op.getValue()); }
+
+void MIRInterpreter::visit(ops::ConcatOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto outputs = allocateOutputTensors(op);
+ Concat(inputs, op.getAxis(), outputs[0]);
+}
+
+void MIRInterpreter::visit(ops::Conv2DOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto outputs = allocateOutputTensors(op);
+ const mir::TensorVariant *bias = nullptr;
+ if (inputs.size() > 2)
+ {
+ bias = &(inputs[2].get());
+ }
+ Conv2D(inputs[0], inputs[1], op.getAttributes(), outputs[0], bias);
+}
+
+void MIRInterpreter::visit(ops::MaxPool2DOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto outputs = allocateOutputTensors(op);
+ MaxPool2D(inputs[0], op, outputs[0]);
+}
+
+void MIRInterpreter::visit(ops::ReshapeOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto outputs = allocateOutputTensors(op);
+ Reshape(inputs[0], outputs[0]);
+}
+
+void MIRInterpreter::visit(ops::ReluOp &op)
+{
+ auto args = getInputTensors(op);
+ auto results = allocateOutputTensors(op);
+ ReLU(args[0], results[0]);
+}
+
+void MIRInterpreter::visit(ops::SigmoidOp &op)
+{
+ auto args = getInputTensors(op);
+ auto results = allocateOutputTensors(op);
+ Sigmoid(args[0], results[0]);
+}
+
+void MIRInterpreter::visit(ops::SoftmaxOp &op)
+{
+ auto inputs = getInputTensors(op);
+ assert(inputs.size() == 1);
+ auto outputs = allocateOutputTensors(op);
+ Softmax(inputs[0], op.getAxis(), outputs[0]);
+}
+
+void MIRInterpreter::visit(ops::FullyConnectedOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto outputs = allocateOutputTensors(op);
+ const mir::TensorVariant *bias = nullptr;
+ if (inputs.size() > 2)
+ {
+ bias = &(inputs[3].get());
+ }
+ FullyConnected(inputs[0], inputs[1], op, outputs[0], bias);
+}
+
+void MIRInterpreter::visit(ops::CappedReluOp &op)
+{
+ auto args = getInputTensors(op);
+ auto results = allocateOutputTensors(op);
+ CappedReLU(args[0], op.getCap(), results[0]);
+}
+
+void MIRInterpreter::visit(ops::DepthwiseConv2DOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto outputs = allocateOutputTensors(op);
+ const mir::TensorVariant *bias = nullptr;
+ if (inputs.size() > 2)
+ {
+ bias = &inputs[3].get();
+ }
+ DepthwiseConv2D(op, inputs[0], inputs[1], outputs[0], bias);
+}
+
+void MIRInterpreter::visit(ops::SliceOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto input = inputs[0];
+ auto outputs = allocateOutputTensors(op);
+ Slice(input, op.getStarts(), outputs[0]);
+}
+
+void MIRInterpreter::visit(ops::TanhOp &op)
+{
+ auto args = getInputTensors(op);
+ auto results = allocateOutputTensors(op);
+ Tanh(args[0], results[0]);
+}
+
+void MIRInterpreter::visit(ops::DeConv2DOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto outputs = allocateOutputTensors(op);
+ DeConv2D(inputs[0], inputs[1], op.getAttributes(), outputs[0]);
+}
+
+void MIRInterpreter::visit(ops::EluOp &op)
+{
+ auto args = getInputTensors(op);
+ auto results = allocateOutputTensors(op);
+ ELU(args[0], op.getAlpha(), results[0]);
+}
+
+void MIRInterpreter::visit(ops::SqueezeOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto outputs = allocateOutputTensors(op);
+ // Squeeze is just a special case of reshape.
+ Reshape(inputs[0], outputs[0]);
+}
+
+void MIRInterpreter::visit(ops::PadOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto outputs = allocateOutputTensors(op);
+ Pad(inputs[0], op, outputs[0]);
+}
+
+void MIRInterpreter::visit(ops::SqrtOp &op)
+{
+ auto args = getInputTensors(op);
+ auto results = allocateOutputTensors(op);
+ Sqrt(args[0], results[0]);
+}
+
+void MIRInterpreter::visit(ops::ResizeOp &op)
+{
+ // TODO support types other than float32
+ auto inputs = getInputTensors(op);
+ assert(inputs[0].get().getElementType() == mir::DataType::FLOAT32);
+ auto outputs = allocateOutputTensors(op);
+
+ Tensor<float> input(inputs[0]);
+ assert(op.getMode() == ops::ResizeOp::ResizeMethod::nearestNeighbor);
+
+ auto scales = op.getScales();
+ Fill(outputs[0], [&scales, &input](const Index &id) {
+ Index in_idx;
+ in_idx.resize(4);
+ for (int i = 0; i < input.getShape().rank(); i++)
+ {
+ in_idx.at(i) = static_cast<int>(floorf(id.at(i) / scales[i]));
+ }
+ return input.at(in_idx);
+ });
+}
+
+void MIRInterpreter::visit(ops::ReduceMeanOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto outputs = allocateOutputTensors(op);
+ ReduceMean(inputs[0], op, outputs[0]);
+}
+
+void MIRInterpreter::visit(ops::TransposeOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto outputs = allocateOutputTensors(op);
+ Transpose(inputs[0], op, outputs[0]);
+}
+
+void MIRInterpreter::visit(ops::GatherOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto outputs = allocateOutputTensors(op);
+ Gather(inputs[0], inputs[1], op, outputs[0]);
+}
+
+void MIRInterpreter::visit(ops::LeakyReluOp &op)
+{
+ auto args = getInputTensors(op);
+ auto results = allocateOutputTensors(op);
+ LeakyReLU(args[0], op.getAlpha(), results[0]);
+}
+
+void MIRInterpreter::visit(ops::OutputOp &op)
+{
+ assert(_tensors.find(op.getInput(0)) != _tensors.end());
+}
+
+void MIRInterpreter::visit(ops::AddOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto outputs = allocateOutputTensors(op);
+ Add(inputs[0], inputs[1], outputs[0]);
+}
+
+void MIRInterpreter::visit(mir::ops::DivOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto outputs = allocateOutputTensors(op);
+ Div(inputs[0], inputs[1], outputs[0]);
+}
+
+void MIRInterpreter::visit(mir::ops::MaxOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto outputs = allocateOutputTensors(op);
+ Max(inputs[0], inputs[1], outputs[0]);
+}
+
+void MIRInterpreter::visit(mir::ops::MulOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto outputs = allocateOutputTensors(op);
+ Mul(inputs[0], inputs[1], outputs[0]);
+}
+
+void MIRInterpreter::visit(mir::ops::SubOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto outputs = allocateOutputTensors(op);
+ Sub(inputs[0], inputs[1], outputs[0]);
+}
+
+void MIRInterpreter::visit(mir::ops::DequantizeOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto outputs = allocateOutputTensors(op);
+ Dequantize(inputs[0], outputs[0]);
+}
+
+void MIRInterpreter::visit(mir::ops::QuantizeOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto outputs = allocateOutputTensors(op);
+ Quantize(inputs[0], outputs[0]);
+}
+
+void MIRInterpreter::visit(mir::ops::HardSwishOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto outputs = allocateOutputTensors(op);
+ HardSwish(inputs[0], outputs[0]);
+}
+
+void MIRInterpreter::visit(mir::ops::GreaterOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto outputs = allocateOutputTensors(op);
+ Greater(inputs[0], inputs[1], outputs[0]);
+}
+
+void MIRInterpreter::visit(mir::ops::LessOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto outputs = allocateOutputTensors(op);
+ Less(inputs[0], inputs[1], outputs[0]);
+}
+
+void MIRInterpreter::visit(mir::ops::EqualOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto outputs = allocateOutputTensors(op);
+ Equal(inputs[0], inputs[1], outputs[0]);
+}
+
+void MIRInterpreter::visit(mir::ops::AbsOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto outputs = allocateOutputTensors(op);
+ Abs(inputs[0], outputs[0]);
+}
+
+void MIRInterpreter::visit(mir::ops::BroadcastOp &op)
+{
+ auto inputs = getInputTensors(op);
+ auto outputs = allocateOutputTensors(op);
+ outputs[0].get() = TensorVariant{inputs[0], op.getOutputShape(0)};
+}
+
+void MIRInterpreter::visit_fallback(mir::Operation &) { throw std::runtime_error("NYI operation"); }
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/Abs.cpp b/compiler/mir-interpreter/src/ops/Abs.cpp
new file mode 100644
index 000000000..547009ffd
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Abs.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2020 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 "Abs.h"
+#include "Common.h"
+
+#include <mir/ShapeRange.h>
+#include <mir/Tensor.h>
+
+#include <cmath>
+
+namespace mir_interpreter
+{
+
+template <typename T> struct AbsImpl
+{
+ static void run(const mir::TensorVariant &arg, mir::TensorVariant &result)
+ {
+ mir::Tensor<T> arg_accessor(arg);
+ mir::Tensor<T> res_accessor(result);
+
+ for (const auto &index : mir::ShapeRange(result.getShape()))
+ {
+ res_accessor.at(index) = std::abs(arg_accessor.at(index));
+ }
+ }
+};
+
+template <> struct AbsImpl<uint8_t>
+{
+ static void run(const mir::TensorVariant &arg, mir::TensorVariant &result)
+ {
+ throw std::runtime_error{"NYI"};
+ }
+};
+
+void Abs(const mir::TensorVariant &arg, mir::TensorVariant &result)
+{
+ dispatch<AbsImpl>(arg.getElementType(), arg, result);
+};
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/Abs.h b/compiler/mir-interpreter/src/ops/Abs.h
new file mode 100644
index 000000000..1ba59e647
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Abs.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2020 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 _NNC_CORE_BACKEND_INTERPRETER_ABS_
+#define _NNC_CORE_BACKEND_INTERPRETER_ABS_
+
+#include <mir/TensorVariant.h>
+
+namespace mir_interpreter
+{
+
+void Abs(const mir::TensorVariant &arg, mir::TensorVariant &result);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_ABS_
diff --git a/compiler/mir-interpreter/src/ops/Add.cpp b/compiler/mir-interpreter/src/ops/Add.cpp
new file mode 100644
index 000000000..631b854b7
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Add.cpp
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2019 The TensorFlow Authors. 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 "Add.h"
+#include "Common.h"
+
+#include "QuantizationHelpers.h"
+#include "mir/Tensor.h"
+#include "mir/ShapeRange.h"
+
+#include <cmath>
+
+namespace mir_interpreter
+{
+
+using namespace mir;
+
+template <typename T> struct AddImpl
+{
+ static void run(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res);
+};
+
+template <typename T>
+void AddImpl<T>::run(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res)
+{
+ TensorVariant broadcasted_lhs(lhs, res.getShape());
+ TensorVariant broadcasted_rhs(rhs, res.getShape());
+ Tensor<T> lhs_accessor(broadcasted_lhs);
+ Tensor<T> rhs_accessor(broadcasted_rhs);
+ Tensor<T> res_accessor(res);
+
+ for (const auto &index : ShapeRange(res.getShape()))
+ {
+ res_accessor.at(index) = lhs_accessor.at(index) + rhs_accessor.at(index);
+ }
+}
+
+template <> struct AddImpl<uint8_t>
+{
+ static void run(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res);
+};
+
+void AddImpl<uint8_t>::run(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res)
+{
+ const auto &lhs_type = lhs.getType();
+ const auto &rhs_type = rhs.getType();
+ const auto &res_type = res.getType();
+
+ assert(lhs_type.isQuantized());
+ assert(rhs_type.isQuantized());
+ assert(res_type.isQuantized());
+
+ int32_t lhs_offset = -lhs_type.getQuantization().getZeroPoint();
+ int32_t rhs_offset = -rhs_type.getQuantization().getZeroPoint();
+ int32_t output_offset = res_type.getQuantization().getZeroPoint();
+
+ double lhs_scale = lhs_type.getQuantization().getScale();
+ double rhs_scale = rhs_type.getQuantization().getScale();
+ double output_scale = res_type.getQuantization().getScale();
+
+ int left_shift = 20;
+ const double twice_max_input_scale = 2 * std::max(lhs_scale, rhs_scale);
+ const double real_lhs_multiplier = lhs_scale / twice_max_input_scale;
+ const double real_rhs_multiplier = rhs_scale / twice_max_input_scale;
+ const double real_output_multiplier = twice_max_input_scale / ((1 << left_shift) * output_scale);
+
+ int32_t lhs_multiplier = 0;
+ int32_t rhs_multiplier = 0;
+ int32_t output_multiplier = 0;
+ int lhs_shift = 0;
+ int rhs_shift = 0;
+ int output_shift = 0;
+
+ QuantizeMultiplierSmallerThanOneExp(real_lhs_multiplier, &lhs_multiplier, &lhs_shift);
+ QuantizeMultiplierSmallerThanOneExp(real_rhs_multiplier, &rhs_multiplier, &rhs_shift);
+ QuantizeMultiplierSmallerThanOneExp(real_output_multiplier, &output_multiplier, &output_shift);
+
+ TensorVariant broadcasted_lhs(lhs, res_type.getShape());
+ TensorVariant broadcasted_rhs(rhs, res_type.getShape());
+
+ Tensor<uint8_t> lhs_accessor(broadcasted_lhs);
+ Tensor<uint8_t> rhs_accessor(broadcasted_rhs);
+ Tensor<uint8_t> res_accessor(res);
+
+ int32_t output_min = std::numeric_limits<uint8_t>::min();
+ int32_t output_max = std::numeric_limits<uint8_t>::max();
+
+ for (const auto &index : ShapeRange(res_type.getShape()))
+ {
+ const int32_t lhs_val = lhs_accessor.at(index) + lhs_offset;
+ const int32_t rhs_val = rhs_accessor.at(index) + rhs_offset;
+ const int32_t shifted_lhs_val = lhs_val * (1 << left_shift);
+ const int32_t shifted_rhs_val = rhs_val * (1 << left_shift);
+ const int32_t scaled_lhs_val =
+ MultiplyByQuantizedMultiplierSmallerThanOneExp(shifted_lhs_val, lhs_multiplier, lhs_shift);
+ const int32_t scaled_rhs_val =
+ MultiplyByQuantizedMultiplierSmallerThanOneExp(shifted_rhs_val, rhs_multiplier, rhs_shift);
+ const int32_t raw_sum = scaled_lhs_val + scaled_rhs_val;
+ const int32_t raw_output =
+ MultiplyByQuantizedMultiplierSmallerThanOneExp(raw_sum, output_multiplier, output_shift) +
+ output_offset;
+ const int32_t clamped_output = std::min(output_max, std::max(output_min, raw_output));
+ res_accessor.at(index) = static_cast<uint8_t>(clamped_output);
+ }
+}
+
+void Add(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res)
+{
+ if (lhs.getElementType() != rhs.getElementType())
+ {
+ throw std::runtime_error{"Add with different input types is unsupported"};
+ }
+ dispatch<AddImpl>(res.getElementType(), lhs, rhs, res);
+}
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/Add.h b/compiler/mir-interpreter/src/ops/Add.h
new file mode 100644
index 000000000..48508226f
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Add.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 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 _NNC_CORE_BACKEND_INTERPRETER_ADD_
+#define _NNC_CORE_BACKEND_INTERPRETER_ADD_
+
+#include "mir/TensorVariant.h"
+
+namespace mir_interpreter
+{
+
+void Add(const mir::TensorVariant &lhs, const mir::TensorVariant &rhs, mir::TensorVariant &res);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_ADD_
diff --git a/compiler/mir-interpreter/src/ops/AvgPool2D.cpp b/compiler/mir-interpreter/src/ops/AvgPool2D.cpp
new file mode 100644
index 000000000..3f1d65100
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/AvgPool2D.cpp
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2019 The TensorFlow Authors. 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 "AvgPool2D.h"
+#include "Common.h"
+
+#include "mir/ShapeRange.h"
+#include "mir/Tensor.h"
+
+namespace mir_interpreter
+{
+
+using namespace mir;
+
+template <typename T> class AvgPool2DImpl
+{
+public:
+ static void run(const mir::ops::AvgPool2DOp &op, const mir::TensorVariant &input_var,
+ mir::TensorVariant &output);
+};
+
+template <typename T>
+void AvgPool2DImpl<T>::run(const ops::AvgPool2DOp &op, const TensorVariant &input_var,
+ TensorVariant &output)
+{
+ const auto &input_shape = op.getInputShape(0);
+ const auto &output_shape = op.getOutputShape(0);
+ const auto &window_size = op.getWindowSize();
+ const auto &strides = op.getStrides();
+ const auto &padding_before = op.getPaddingBefore();
+ const auto &padding_after = op.getPaddingAfter();
+ (void)padding_after;
+
+ constexpr int num_spatial_dims = 2;
+ assert(input_var.getShape().rank() == 4);
+ assert(window_size.size() == num_spatial_dims);
+ assert(strides.size() == num_spatial_dims);
+ assert(padding_before.size() == num_spatial_dims);
+ assert(padding_after.size() == num_spatial_dims);
+
+ Tensor<T> res_accessor(output);
+ Tensor<T> input(input_var);
+
+ ShapeRange in_range(input_shape);
+ Index in_index(input_shape.rank());
+
+ for (const auto &out_index : ShapeRange(output_shape))
+ {
+ T result = 0;
+ size_t num_elements = 0;
+
+ // Assuming NHWC format.
+ in_index.at(0) = out_index.at(0);
+ in_index.at(3) = out_index.at(3);
+
+ for (const auto &window_index : ShapeRange(Shape(window_size)))
+ {
+ // Assuming NHWC format.
+ for (int i = 0; i < num_spatial_dims; ++i)
+ in_index.at(1 + i) =
+ out_index.at(1 + i) * strides[i] + window_index.at(i) - padding_before[i];
+
+ if (in_range.contains(in_index))
+ {
+ num_elements++;
+ result += input.at(in_index);
+ }
+ else if (op.getIncludePad())
+ {
+ num_elements++;
+ }
+ }
+
+ result /= num_elements;
+ res_accessor.at(out_index) = result;
+ }
+}
+
+template <> struct AvgPool2DImpl<uint8_t>
+{
+ static void run(const mir::ops::AvgPool2DOp &op, const mir::TensorVariant &input,
+ mir::TensorVariant &output);
+};
+
+void AvgPool2DImpl<uint8_t>::run(const ops::AvgPool2DOp &op, const TensorVariant &input,
+ TensorVariant &output)
+{
+ const auto &input_type = input.getType();
+ const auto &output_type = op.getOutput(0)->getType();
+ (void)input_type;
+
+ assert(input_type.isQuantized());
+ assert(output_type.isQuantized());
+ assert(input_type.getElementType() == DataType::UINT8);
+
+ const auto &input_shape = op.getInputShape(0);
+ const auto &output_shape = op.getOutputShape(0);
+ const auto &window_size = op.getWindowSize();
+ const auto &strides = op.getStrides();
+ const auto &padding_before = op.getPaddingBefore();
+ const auto &padding_after = op.getPaddingAfter();
+ (void)padding_after;
+
+ constexpr int num_spatial_dims = 2;
+ assert(input.getShape().rank() == 4);
+ assert(window_size.size() == num_spatial_dims);
+ assert(strides.size() == num_spatial_dims);
+ assert(padding_before.size() == num_spatial_dims);
+ assert(padding_after.size() == num_spatial_dims);
+
+ Tensor<uint8_t> input_accessor(input);
+ Tensor<uint8_t> res_accessor(output);
+
+ ShapeRange in_range(input_shape);
+ Index in_index(input_shape.rank());
+
+ int32_t output_min = std::numeric_limits<uint8_t>::min();
+ int32_t output_max = std::numeric_limits<uint8_t>::max();
+
+ for (const auto &out_index : ShapeRange(output_shape))
+ {
+ int32_t result = 0;
+ size_t num_elements = 0;
+
+ // Assuming NHWC format.
+ in_index.at(0) = out_index.at(0);
+ in_index.at(3) = out_index.at(3);
+
+ for (const auto &window_index : ShapeRange(Shape(window_size)))
+ {
+ // Assuming NHWC format.
+ for (int i = 0; i < num_spatial_dims; ++i)
+ in_index.at(1 + i) =
+ out_index.at(1 + i) * strides[i] + window_index.at(i) - padding_before[i];
+
+ if (in_range.contains(in_index))
+ {
+ num_elements++;
+ result += input_accessor.at(in_index);
+ }
+ else if (op.getIncludePad())
+ {
+ num_elements++;
+ }
+ }
+ result = (result + num_elements / 2) / num_elements;
+ result = std::max(result, output_min);
+ result = std::min(result, output_max);
+ res_accessor.at(out_index) = static_cast<uint8_t>(result);
+ }
+}
+
+void AvgPool2D(const mir::ops::AvgPool2DOp &op, const mir::TensorVariant &input,
+ mir::TensorVariant &output)
+{
+ dispatch<AvgPool2DImpl>(output.getElementType(), op, input, output);
+}
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/AvgPool2D.h b/compiler/mir-interpreter/src/ops/AvgPool2D.h
new file mode 100644
index 000000000..b30574cee
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/AvgPool2D.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2019 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 _NNC_CORE_BACKEND_INTERPRETER_AVG_POOL_2D_
+#define _NNC_CORE_BACKEND_INTERPRETER_AVG_POOL_2D_
+
+#include "mir/ops/AvgPool2DOp.h"
+#include "mir/TensorVariant.h"
+
+namespace mir_interpreter
+{
+
+void AvgPool2D(const mir::ops::AvgPool2DOp &op, const mir::TensorVariant &input,
+ mir::TensorVariant &output);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_AVG_POOL_2D_
diff --git a/compiler/mir-interpreter/src/ops/CappedReLU.cpp b/compiler/mir-interpreter/src/ops/CappedReLU.cpp
new file mode 100644
index 000000000..1ac95ac16
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/CappedReLU.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2019 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 "CappedReLU.h"
+
+#include "mir/ShapeRange.h"
+#include "mir/Tensor.h"
+
+#include "Common.h"
+
+#include <algorithm>
+#include <cstdint>
+
+namespace mir_interpreter
+{
+
+template <typename T> struct CappedReLUImpl
+{
+ static void run(const mir::TensorVariant &arg, float cap, mir::TensorVariant &result);
+};
+
+template <typename T>
+void CappedReLUImpl<T>::run(const mir::TensorVariant &arg, float cap, mir::TensorVariant &result)
+{
+ mir::Tensor<T> arg_accessor(arg);
+ mir::Tensor<T> res_accessor(result);
+
+ for (const auto &index : mir::ShapeRange(result.getShape()))
+ {
+ res_accessor.at(index) = std::min(std::max(arg_accessor.at(index), T(0)), static_cast<T>(cap));
+ }
+}
+
+static float dequantize(uint8_t x, const mir::AffineQuantization &q)
+{
+ return (static_cast<int>(x) - q.getZeroPoint()) * q.getScale();
+}
+
+static uint8_t quantize(float x, const mir::AffineQuantization &q)
+{
+ return (static_cast<float>(x) / q.getScale() + q.getZeroPoint());
+}
+
+template <> struct CappedReLUImpl<uint8_t>
+{
+ static void run(const mir::TensorVariant &arg, float cap, mir::TensorVariant &result)
+ {
+ mir::Tensor<uint8_t> arg_accessor(arg);
+ mir::Tensor<uint8_t> res_accessor(result);
+
+ auto quant_info = arg.getType().getQuantization();
+ assert(!quant_info.empty());
+
+ for (const auto &index : mir::ShapeRange(result.getShape()))
+ {
+ auto value = dequantize(arg_accessor.at(index), quant_info);
+ auto out_value =
+ quantize(std::min(std::max(value, 0.0f), cap), result.getType().getQuantization());
+ res_accessor.at(index) = out_value;
+ }
+ }
+};
+
+void CappedReLU(const mir::TensorVariant &arg, float cap, mir::TensorVariant &result)
+{
+ dispatch<CappedReLUImpl>(arg.getElementType(), arg, cap, result);
+}
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/CappedReLU.h b/compiler/mir-interpreter/src/ops/CappedReLU.h
new file mode 100644
index 000000000..ffb756d2a
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/CappedReLU.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 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 _NNC_CORE_BACKEND_INTERPRETER_CAPPEDRELU_
+#define _NNC_CORE_BACKEND_INTERPRETER_CAPPEDRELU_
+
+#include <mir/TensorVariant.h>
+
+namespace mir_interpreter
+{
+
+void CappedReLU(const mir::TensorVariant &arg, float cap, mir::TensorVariant &result);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_CAPPEDRELU_
diff --git a/compiler/mir-interpreter/src/ops/Common.cpp b/compiler/mir-interpreter/src/ops/Common.cpp
new file mode 100644
index 000000000..dae207f2e
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Common.cpp
@@ -0,0 +1,37 @@
+/*
+ * 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 <cassert>
+
+#include "Common.h"
+
+namespace mir_interpreter
+{
+
+using namespace mir;
+
+Index shift(const Index &in_index, const Shape &shift_from)
+{
+ Index index = in_index;
+ assert(index.rank() == shift_from.rank());
+ for (int32_t d = 0; d < in_index.rank(); ++d)
+ {
+ index.at(d) = index.at(d) + shift_from.dim(d);
+ }
+ return index;
+}
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/Common.h b/compiler/mir-interpreter/src/ops/Common.h
new file mode 100644
index 000000000..43336216e
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Common.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2020 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 _NNC_CORE_BACKEND_INTERPRETER_COMMON_
+#define _NNC_CORE_BACKEND_INTERPRETER_COMMON_
+
+#include "mir/Tensor.h"
+#include "mir/TensorVariant.h"
+#include "mir/DataType.h"
+#include "mir/Shape.h"
+#include "mir/Index.h"
+
+namespace mir_interpreter
+{
+
+template <template <typename> class F, typename... Args>
+void dispatch(mir::DataType dt, Args &&... args)
+{
+ switch (dt)
+ {
+ case mir::DataType::FLOAT32:
+ return F<float>::run(std::forward<Args>(args)...);
+ case mir::DataType::FLOAT64:
+ return F<double>::run(std::forward<Args>(args)...);
+ case mir::DataType::INT32:
+ return F<int32_t>::run(std::forward<Args>(args)...);
+ case mir::DataType::INT64:
+ return F<int64_t>::run(std::forward<Args>(args)...);
+ case mir::DataType::UINT8:
+ return F<uint8_t>::run(std::forward<Args>(args)...);
+ case mir::DataType::UNKNOWN:
+ throw std::runtime_error{"Unknown datatype met during operation execution"};
+ default:
+ throw std::runtime_error{"mir::DataType enum mismatch"};
+ }
+}
+
+template <typename T> void erase(mir::TensorVariant &tv)
+{
+ size_t element_count = tv.getShape().numElements();
+ for (size_t i = 0; i < element_count; ++i)
+ {
+ auto ptr = tv.atOffset(i);
+ *reinterpret_cast<T *>(ptr) = 0;
+ }
+}
+
+mir::Index shift(const mir::Index &in_index, const mir::Shape &shift_from);
+
+} // namespace mir_interpreter
+
+#endif // _NNC_CORE_BACKEND_INTERPRETER_COMMON_
diff --git a/compiler/mir-interpreter/src/ops/Concat.cpp b/compiler/mir-interpreter/src/ops/Concat.cpp
new file mode 100644
index 000000000..99fe00c31
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Concat.cpp
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2019 The TensorFlow Authors. 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 "Concat.h"
+#include "Common.h"
+
+#include <cmath>
+#include <cstring>
+
+namespace mir_interpreter
+{
+
+template <typename T> struct ConcatImpl
+{
+ static void run(const std::vector<std::reference_wrapper<const mir::TensorVariant>> &inputs,
+ int axis, mir::TensorVariant &output);
+};
+
+template <typename T>
+void ConcatImpl<T>::run(const std::vector<std::reference_wrapper<const mir::TensorVariant>> &inputs,
+ int axis, mir::TensorVariant &output)
+{
+ const auto &output_shape = output.getShape();
+ const size_t inputs_count = inputs.size();
+ const int32_t concat_dims = output_shape.rank();
+ int64_t concat_size = 0;
+ for (size_t i = 0; i < inputs_count; i++)
+ {
+ const auto &input_shape = inputs[i].get().getShape();
+ assert(input_shape.rank() == concat_dims);
+ for (int32_t j = 0; j < concat_dims; j++)
+ {
+ if (j != axis)
+ {
+ assert(input_shape.dim(j) == output_shape.dim(j));
+ }
+ }
+ concat_size += input_shape.dim(axis);
+ }
+ assert(concat_size == output_shape.dim(axis));
+ // Outer size before axis
+ int32_t outer_size = 1;
+ for (int32_t i = 0; i < axis; i++)
+ outer_size *= output_shape.dim(i);
+ // Inner size after axis
+ int32_t base_inner_size = 1;
+ for (int32_t i = axis + 1; i < concat_dims; i++)
+ base_inner_size *= output_shape.dim(i);
+ // flatten = outer_size * dim(axis) * base_inner_size;
+ std::vector<int32_t> copy_sizes;
+ std::vector<char *> input_ptrs;
+ for (size_t i = 0; i < inputs_count; i++)
+ {
+ const auto input_shape = inputs[i].get().getShape();
+ copy_sizes.push_back(input_shape.dim(axis) * base_inner_size);
+ input_ptrs.push_back(inputs[i].get().atOffset(0));
+ }
+
+ char *output_ptr = output.atOffset(0);
+ const size_t elem_size = inputs[0].get().getElementSize();
+ for (int32_t i = 0; i < outer_size; i++)
+ {
+ for (size_t j = 0; j < inputs_count; j++)
+ {
+ std::memcpy(output_ptr, input_ptrs[j], copy_sizes[j] * elem_size);
+ output_ptr += copy_sizes[j] * elem_size;
+ input_ptrs[j] += copy_sizes[j] * elem_size;
+ }
+ }
+}
+
+template <> struct ConcatImpl<uint8_t>
+{
+ static void run(const std::vector<std::reference_wrapper<const mir::TensorVariant>> &inputs,
+ int axis, mir::TensorVariant &output);
+};
+
+void ConcatImpl<uint8_t>::run(
+ const std::vector<std::reference_wrapper<const mir::TensorVariant>> &inputs, int axis,
+ mir::TensorVariant &output)
+{
+ const size_t inputs_count = inputs.size();
+ std::vector<int32_t> input_zeropoints(inputs_count);
+ std::vector<float> input_scales(inputs_count);
+ const auto &output_shape = output.getShape();
+ const int32_t concat_dimensions = output_shape.rank();
+ int64_t concat_size = 0;
+ for (size_t i = 0; i < inputs_count; i++)
+ {
+ const auto &input_type = inputs[i].get().getType();
+ assert(input_type.isQuantized());
+ assert(input_type.getElementType() == mir::DataType::UINT8);
+ const auto &input_shape = input_type.getShape();
+ assert(input_shape.rank() == concat_dimensions);
+
+ for (int32_t j = 0; j < concat_dimensions; j++)
+ if (j != axis)
+ assert(input_shape.dim(j) == output_shape.dim(j));
+
+ concat_size += input_shape.dim(axis);
+ input_zeropoints[i] = input_type.getQuantization().getZeroPoint();
+ input_scales[i] = input_type.getQuantization().getScale();
+ }
+ assert(concat_size == output_shape.dim(axis));
+
+ const auto &output_type = output.getType();
+ assert(output_type.isQuantized());
+ int32_t output_zeropoint = output_type.getQuantization().getZeroPoint();
+ float output_scale = output_type.getQuantization().getScale();
+
+ // Outer size before axis
+ int32_t outer_size = 1;
+ for (int32_t i = 0; i < axis; i++)
+ outer_size *= output_shape.dim(i);
+ // Inner size after axis
+ int32_t base_inner_size = 1;
+ for (int32_t i = axis + 1; i < concat_dimensions; i++)
+ base_inner_size *= output_shape.dim(i);
+ // flatten = outer_size * dim(axis) * base_inner_size;
+
+ uint8_t *output_ptr = reinterpret_cast<uint8_t *>(output.atOffset(0));
+
+ const float inverse_output_scale = 1.f / output_scale;
+ for (int k = 0; k < outer_size; k++)
+ {
+ for (size_t i = 0; i < inputs_count; ++i)
+ {
+ const mir::TensorVariant &input = inputs[i];
+ const int copy_size = input.getShape().dim(axis) * base_inner_size;
+ const char *input_data = input.atOffset(0) + k * copy_size;
+ const uint8_t *input_ptr = reinterpret_cast<const uint8_t *>(input_data);
+ if (input_zeropoints[i] == output_zeropoint && input_scales[i] == output_scale)
+ {
+ std::memcpy(output_ptr, input_ptr, copy_size);
+ }
+ else
+ {
+ const float scale = input_scales[i] * inverse_output_scale;
+ const float bias = -input_zeropoints[i] * scale;
+ for (int j = 0; j < copy_size; ++j)
+ {
+ const int32_t value =
+ static_cast<int32_t>(std::round(input_ptr[j] * scale + bias)) + output_zeropoint;
+ output_ptr[j] = static_cast<uint8_t>(std::max(std::min(255, value), 0));
+ }
+ }
+ output_ptr += copy_size;
+ }
+ }
+}
+
+void Concat(const std::vector<std::reference_wrapper<const mir::TensorVariant>> &inputs, int axis,
+ mir::TensorVariant &output)
+{
+ dispatch<ConcatImpl>(inputs[0].get().getElementType(), inputs, axis, output);
+}
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/Concat.h b/compiler/mir-interpreter/src/ops/Concat.h
new file mode 100644
index 000000000..587a97809
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Concat.h
@@ -0,0 +1,30 @@
+/*
+ * 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 _NNC_CORE_BACKEND_INTERPRETER_CONCAT_IMPL_
+#define _NNC_CORE_BACKEND_INTERPRETER_CONCAT_IMPL_
+
+#include <mir/TensorVariant.h>
+
+namespace mir_interpreter
+{
+
+void Concat(const std::vector<std::reference_wrapper<const mir::TensorVariant>> &inputs, int axis,
+ mir::TensorVariant &output);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_CONCAT_IMPL_
diff --git a/compiler/mir-interpreter/src/ops/Conv2D.cpp b/compiler/mir-interpreter/src/ops/Conv2D.cpp
new file mode 100644
index 000000000..c9b98a56f
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Conv2D.cpp
@@ -0,0 +1,261 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2019 The TensorFlow Authors. 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 "Conv2D.h"
+#include "QuantizationHelpers.h"
+#include "Common.h"
+
+#include "mir/Tensor.h"
+
+#include <cmath>
+
+namespace mir_interpreter
+{
+
+using namespace mir;
+
+static std::int32_t calcOffset(const Shape &shape, std::int32_t i0, std::int32_t i1,
+ std::int32_t i2, std::int32_t i3)
+{
+ return ((i0 * shape.dim(1) + i1) * shape.dim(2) + i2) * shape.dim(3) + i3;
+}
+
+template <typename T> struct Conv2DImpl
+{
+ static void run(const TensorVariant &input, const TensorVariant &kernel,
+ const Conv2DOpAttributes &attributes, TensorVariant &result,
+ const TensorVariant *fused_bias);
+};
+
+template <typename T>
+void Conv2DImpl<T>::run(const TensorVariant &input, const TensorVariant &kernel,
+ const Conv2DOpAttributes &attributes, TensorVariant &result,
+ const TensorVariant *fused_bias)
+{
+ const auto *input_data = reinterpret_cast<const T *>(input.atOffset(0));
+ const auto *kernel_data = reinterpret_cast<const T *>(kernel.atOffset(0));
+ auto *result_data = reinterpret_cast<T *>(result.atOffset(0));
+
+ const Shape &input_shape = input.getShape();
+ const Shape &output_shape = result.getShape();
+ const Shape &kernel_shape = kernel.getShape();
+
+ const std::vector<std::int32_t> &strides = attributes.strides;
+ const std::vector<std::int32_t> &padding_before = attributes.padding_before;
+ const std::int32_t num_groups = attributes.num_groups;
+ assert(attributes.data_format == DataFormat::NHWC);
+
+ const std::int32_t batch_size = output_shape.dim(0);
+ const std::int32_t output_height = output_shape.dim(1);
+ const std::int32_t output_width = output_shape.dim(2);
+ const std::int32_t kernel_height = kernel_shape.dim(1);
+ const std::int32_t kernel_width = kernel_shape.dim(2);
+ const std::int32_t input_height = input_shape.dim(1);
+ const std::int32_t input_width = input_shape.dim(2);
+
+ const std::int32_t num_in_channels = input_shape.dim(3);
+ const std::int32_t num_out_channels = output_shape.dim(3);
+
+ assert(num_in_channels % num_groups == 0);
+ assert(num_out_channels % num_groups == 0);
+
+ const std::int32_t out_group_size = num_out_channels / num_groups;
+ const std::int32_t in_group_size = num_in_channels / num_groups;
+
+ assert(kernel_shape.dim(3) == in_group_size);
+ assert(kernel_shape.dim(0) == num_out_channels);
+
+ for (std::int32_t batch = 0; batch < batch_size; ++batch)
+ {
+ for (std::int32_t out_y = 0; out_y < output_height; ++out_y)
+ {
+ for (std::int32_t out_x = 0; out_x < output_width; ++out_x)
+ {
+ for (std::int32_t group = 0; group < num_groups; ++group)
+ {
+ const std::int32_t out_group_offset = group * out_group_size;
+ const std::int32_t in_group_offset = group * in_group_size;
+
+ for (std::int32_t out_c = 0; out_c < out_group_size; ++out_c)
+ {
+ const std::int32_t in_y_origin = (out_y * strides[0]) - padding_before[0];
+ const std::int32_t in_x_origin = (out_x * strides[1]) - padding_before[1];
+
+ T sum = 0.0f;
+
+ for (std::int32_t kernel_y = 0; kernel_y < kernel_height; ++kernel_y)
+ {
+ for (std::int32_t kernel_x = 0; kernel_x < kernel_width; ++kernel_x)
+ {
+ for (std::int32_t in_c = 0; in_c < in_group_size; ++in_c)
+ {
+ const std::int32_t in_y = in_y_origin + kernel_y;
+ const std::int32_t in_x = in_x_origin + kernel_x;
+
+ if ((in_y >= 0 && in_y < input_height) && (in_x >= 0 && in_x < input_width))
+ {
+ const std::int32_t in_offset =
+ calcOffset(input_shape, batch, in_y, in_x, in_group_offset + in_c);
+ const std::int32_t kernel_offset = calcOffset(
+ kernel_shape, out_group_offset + out_c, kernel_y, kernel_x, in_c);
+ const T input_val = input_data[in_offset];
+ const T kernel_val = kernel_data[kernel_offset];
+ sum += kernel_val * input_val;
+ }
+ }
+ }
+ }
+
+ const std::int32_t out_offset =
+ calcOffset(output_shape, batch, out_y, out_x, out_group_offset + out_c);
+ result_data[out_offset] = sum;
+ }
+ }
+ }
+ }
+ }
+}
+
+template <> struct Conv2DImpl<uint8_t>
+{
+ static void run(const TensorVariant &input, const TensorVariant &kernel,
+ const Conv2DOpAttributes &attributes, TensorVariant &result,
+ const TensorVariant *fused_bias);
+};
+
+void Conv2DImpl<uint8_t>::run(const TensorVariant &input, const TensorVariant &kernel,
+ const Conv2DOpAttributes &attributes, TensorVariant &result,
+ const TensorVariant *fused_bias)
+{
+ if (!fused_bias)
+ {
+ throw std::runtime_error{"Quantized Conv2D cannot be executed without fused bias"};
+ }
+
+ const auto &input_type = input.getType();
+ const auto &kernel_type = kernel.getType();
+ const auto &bias_type = fused_bias->getType();
+ const auto &output_type = result.getType();
+ (void)bias_type;
+
+ assert(input_type.isQuantized());
+ assert(kernel_type.isQuantized());
+ assert(bias_type.isQuantized());
+ assert(output_type.isQuantized());
+ assert(input_type.getElementType() == DataType::UINT8);
+ assert(kernel_type.getElementType() == DataType::UINT8);
+ assert(bias_type.getElementType() == DataType::INT32);
+ assert(output_type.getElementType() == DataType::UINT8);
+
+ int32_t input_offset = -input_type.getQuantization().getZeroPoint();
+ int32_t kernel_offset = -kernel_type.getQuantization().getZeroPoint();
+ int32_t output_offset = output_type.getQuantization().getZeroPoint();
+
+ double input_scale = input_type.getQuantization().getScale();
+ double kernel_scale = kernel_type.getQuantization().getScale();
+ double output_scale = output_type.getQuantization().getScale();
+
+ double real_multiplier = input_scale * kernel_scale / output_scale;
+ int32_t output_multiplier = 0;
+ int output_shift = 0;
+ QuantizeMultiplier(real_multiplier, &output_multiplier, &output_shift);
+
+ const Shape &in_shape = input.getShape();
+ const Shape &kernel_shape = kernel.getShape();
+ const Shape &out_shape = result.getShape();
+ const auto &strides = attributes.strides;
+ const std::vector<int32_t> &pads = attributes.padding_before;
+ assert(attributes.num_groups == 1);
+ assert(attributes.data_format == DataFormat::NHWC);
+
+ assert(in_shape.rank() == 4);
+ assert(kernel_shape.rank() == 4);
+ assert(kernel_shape.dim(3) == in_shape.dim(3));
+ assert(kernel_shape.dim(0) == out_shape.dim(3));
+ assert(strides.size() == 2);
+ assert(pads.size() == 2);
+
+ int32_t stride_height = strides[0];
+ int32_t stride_width = strides[1];
+
+ int32_t pad_height = pads[0];
+ int32_t pad_width = pads[1];
+
+ int32_t input_height = in_shape.dim(1);
+ int32_t input_width = in_shape.dim(2);
+
+ Tensor<uint8_t> input_accessor(input);
+ Tensor<uint8_t> kernel_accessor(kernel);
+ Tensor<int32_t> bias_accessor(*fused_bias);
+ Tensor<uint8_t> res_accessor(result);
+
+ int32_t output_min = std::numeric_limits<uint8_t>::min();
+ int32_t output_max = std::numeric_limits<uint8_t>::max();
+
+ for (int batch = 0; batch < out_shape.dim(0); ++batch)
+ {
+ for (int out_y = 0; out_y < out_shape.dim(1); ++out_y)
+ {
+ for (int out_x = 0; out_x < out_shape.dim(2); ++out_x)
+ {
+ for (int out_channel = 0; out_channel < out_shape.dim(3); ++out_channel)
+ {
+ const int in_x_origin = (out_x * stride_width) - pad_width;
+ const int in_y_origin = (out_y * stride_height) - pad_height;
+ int32_t acc = 0;
+ for (int filter_y = 0; filter_y < kernel_shape.dim(1); ++filter_y)
+ {
+ for (int filter_x = 0; filter_x < kernel_shape.dim(2); ++filter_x)
+ {
+ for (int in_channel = 0; in_channel < kernel_shape.dim(3); ++in_channel)
+ {
+ const int in_x = in_x_origin + filter_x;
+ const int in_y = in_y_origin + filter_y;
+ // If the location is outside the bounds of the input image,
+ // use zero as a default value.
+ if ((in_x >= 0) && (in_x < input_width) && (in_y >= 0) && (in_y < input_height))
+ {
+ Index in_index{batch, in_y, in_x, in_channel};
+ Index ker_index{out_channel, filter_y, filter_x, in_channel};
+ int32_t input_val = input_accessor.at(in_index);
+ int32_t kernel_val = kernel_accessor.at(ker_index);
+ acc += (kernel_val + kernel_offset) * (input_val + input_offset);
+ }
+ }
+ }
+ }
+ acc += bias_accessor.at(Index{out_channel});
+ acc = MultiplyByQuantizedMultiplier(acc, output_multiplier, output_shift);
+ acc += output_offset;
+ acc = std::max(acc, output_min);
+ acc = std::min(acc, output_max);
+ Index out_index{batch, out_y, out_x, out_channel};
+ res_accessor.at(out_index) = static_cast<uint8_t>(acc);
+ }
+ }
+ }
+ }
+}
+
+void Conv2D(const mir::TensorVariant &input, const mir::TensorVariant &kernel,
+ const mir::Conv2DOpAttributes &attributes, mir::TensorVariant &result,
+ const mir::TensorVariant *fused_bias)
+{
+ dispatch<Conv2DImpl>(result.getElementType(), input, kernel, attributes, result, fused_bias);
+}
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/Conv2D.h b/compiler/mir-interpreter/src/ops/Conv2D.h
new file mode 100644
index 000000000..ebb550816
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Conv2D.h
@@ -0,0 +1,32 @@
+/*
+ * 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 _NNC_CORE_BACKEND_INTERPRETER_CONV2D_IMPL_
+#define _NNC_CORE_BACKEND_INTERPRETER_CONV2D_IMPL_
+
+#include "mir/ops/Conv2DOp.h"
+#include "mir/TensorVariant.h"
+
+namespace mir_interpreter
+{
+
+void Conv2D(const mir::TensorVariant &input, const mir::TensorVariant &kernel,
+ const mir::Conv2DOpAttributes &attributes, mir::TensorVariant &result,
+ const mir::TensorVariant *fused_bias);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_CONV2D_IMPL
diff --git a/compiler/mir-interpreter/src/ops/DeConv2D.cpp b/compiler/mir-interpreter/src/ops/DeConv2D.cpp
new file mode 100644
index 000000000..746d8c87c
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/DeConv2D.cpp
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. 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 "DeConv2D.h"
+#include "Common.h"
+
+#include "mir/TensorUtil.h"
+
+#include <cstdint>
+
+namespace mir_interpreter
+{
+
+using namespace mir;
+using std::int32_t;
+
+static int32_t calcOffset(const Shape &shape, int32_t i0, int32_t i1, int32_t i2, int32_t i3)
+{
+ return ((i0 * shape.dim(1) + i1) * shape.dim(2) + i2) * shape.dim(3) + i3;
+}
+
+template <typename T> struct DeConv2DImpl
+{
+ static void run(const TensorVariant &input, const TensorVariant &kernel,
+ const Deconv2DOpAttributes &attributes, TensorVariant &output);
+};
+
+template <typename T>
+void DeConv2DImpl<T>::run(const TensorVariant &input, const TensorVariant &kernel,
+ const Deconv2DOpAttributes &attributes, TensorVariant &output)
+{
+ // [H, W, Co, Ci] -> [Ci, H, W, Co]
+ TensorVariant transposed_kernel = transposeTensor<3, 0, 1, 2>(kernel);
+
+ const auto *input_data = reinterpret_cast<const T *>(input.atOffset(0));
+ const auto *kernel_data = reinterpret_cast<const T *>(transposed_kernel.atOffset(0));
+ auto *output_data = reinterpret_cast<T *>(output.atOffset(0));
+
+ const Shape &input_shape = input.getShape();
+ const Shape &output_shape = output.getShape();
+ const Shape &kernel_shape = transposed_kernel.getShape();
+
+ const std::vector<int32_t> &strides = attributes.strides;
+ const std::vector<int32_t> &padding_before = attributes.padding_before;
+ assert(attributes.data_format == DataFormat::NHWC);
+
+ const int32_t batch_size = output_shape.dim(0);
+ const int32_t output_height = output_shape.dim(1);
+ const int32_t output_width = output_shape.dim(2);
+ const int32_t kernel_height = kernel_shape.dim(1);
+ const int32_t kernel_width = kernel_shape.dim(2);
+ const int32_t input_height = input_shape.dim(1);
+ const int32_t input_width = input_shape.dim(2);
+
+ const int32_t num_in_channels = input_shape.dim(3);
+ const int32_t num_out_channels = output_shape.dim(3);
+
+ assert(kernel_shape.dim(0) == num_in_channels);
+ assert(kernel_shape.dim(3) == num_out_channels);
+
+ erase<T>(output);
+
+ for (int32_t batch = 0; batch < batch_size; ++batch)
+ {
+ for (int32_t in_y = 0; in_y < input_height; ++in_y)
+ {
+ for (int32_t in_x = 0; in_x < input_width; ++in_x)
+ {
+ for (int32_t in_c = 0; in_c < num_in_channels; ++in_c)
+ {
+ const T input_val = input_data[calcOffset(input_shape, batch, in_y, in_x, in_c)];
+ const int32_t out_y_origin = in_y * strides[0] - padding_before[0];
+ const int32_t out_x_origin = in_x * strides[1] - padding_before[1];
+
+ for (int32_t kernel_y = 0; kernel_y < kernel_height; ++kernel_y)
+ {
+ for (int32_t kernel_x = 0; kernel_x < kernel_width; ++kernel_x)
+ {
+ const int32_t out_y = out_y_origin + kernel_y;
+ const int32_t out_x = out_x_origin + kernel_x;
+
+ if ((out_y >= 0 && out_y < output_height) && (out_x >= 0 && out_x < output_width))
+ {
+ for (int32_t out_c = 0; out_c < num_out_channels; ++out_c)
+ {
+ const int32_t kernel_offset =
+ calcOffset(kernel_shape, in_c, kernel_y, kernel_x, out_c);
+ const int32_t output_offset =
+ calcOffset(output_shape, batch, out_y, out_x, out_c);
+ const T kernel_val = kernel_data[kernel_offset];
+ output_data[output_offset] += input_val * kernel_val;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+void DeConv2D(const TensorVariant &input, const TensorVariant &kernel,
+ const Deconv2DOpAttributes &attributes, TensorVariant &output)
+{
+ dispatch<DeConv2DImpl>(output.getElementType(), input, kernel, attributes, output);
+}
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/DeConv2D.h b/compiler/mir-interpreter/src/ops/DeConv2D.h
new file mode 100644
index 000000000..be797fcef
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/DeConv2D.h
@@ -0,0 +1,41 @@
+/*
+ * 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 _NNC_CORE_BACKEND_INTERPRETER_DECONV2D_IMPL_
+#define _NNC_CORE_BACKEND_INTERPRETER_DECONV2D_IMPL_
+
+#include "mir/Attributes.h"
+#include "mir/TensorVariant.h"
+
+namespace mir_interpreter
+{
+
+/**
+ * @brief Transposed convolution (or Deconvolution)
+ * @param input The Input tensor
+ * @param op The DeConvolution operation object
+ *
+ * This is basically the backward pass for the convolution operation,
+ * hence all the indexing can be deducted by expressing the input index
+ * of Conv in terms of it's output index.
+ */
+
+void DeConv2D(const mir::TensorVariant &input, const mir::TensorVariant &kernel,
+ const mir::Deconv2DOpAttributes &attributes, mir::TensorVariant &output);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_DECONV2D_IMPL_
diff --git a/compiler/mir-interpreter/src/ops/DepthwiseConv2D.cpp b/compiler/mir-interpreter/src/ops/DepthwiseConv2D.cpp
new file mode 100644
index 000000000..4b6df3478
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/DepthwiseConv2D.cpp
@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2019 The TensorFlow Authors. 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 "DepthwiseConv2D.h"
+#include "QuantizationHelpers.h"
+#include "Common.h"
+
+#include "mir/ShapeRange.h"
+#include "mir/Tensor.h"
+
+#include <cmath>
+
+namespace mir_interpreter
+{
+
+using namespace mir;
+
+template <typename T> struct DepthwiseConv2DImpl
+{
+ static void run(const mir::ops::DepthwiseConv2DOp &op, const mir::TensorVariant &inputv,
+ const mir::TensorVariant &kernelv, const mir::TensorVariant *biasv,
+ mir::TensorVariant &output);
+};
+
+template <typename T>
+void DepthwiseConv2DImpl<T>::run(const mir::ops::DepthwiseConv2DOp &op,
+ const mir::TensorVariant &inputv,
+ const mir::TensorVariant &kernelv, const mir::TensorVariant *biasv,
+ mir::TensorVariant &output)
+{
+ const Shape &in_shape = op.getInputShape(0);
+ const Shape &kernel_shape = op.getInputShape(1);
+ const Shape &out_shape = op.getOutputShape(0);
+ const auto &strides = op.getStrides();
+ const std::vector<int32_t> &pads = op.getPaddingBefore();
+
+ assert(in_shape.rank() == 4);
+ assert(kernel_shape.rank() == 4);
+ assert(kernel_shape.dim(2) == in_shape.dim(3));
+ assert(in_shape.dim(3) * kernel_shape.dim(3) == out_shape.dim(3));
+ assert(strides.size() == 2);
+ assert(pads.size() == 2);
+
+ int32_t channel_multiplier = kernel_shape.dim(3);
+
+ Tensor<T> res_accessor(output);
+ Tensor<T> input(inputv);
+ Tensor<T> bias(*biasv);
+ Tensor<T> kernel(kernelv);
+
+ ShapeRange in_range(in_shape);
+ ShapeRange kernel_range(kernel_shape);
+ ShapeRange out_range(Shape{out_shape.dim(0), out_shape.dim(1), out_shape.dim(2), 1});
+
+ Index in_index;
+ in_index.resize(4);
+
+ erase<T>(output);
+
+ for (const auto &out_index : out_range)
+ {
+ Index out_index_k = out_index;
+ for (const auto &kernel_index : kernel_range)
+ {
+ in_index.at(0) = out_index.at(0);
+ for (int i = 0; i < 2; ++i)
+ in_index.at(1 + i) = out_index.at(1 + i) * strides[i] + kernel_index.at(i) - pads[i];
+ in_index.at(3) = kernel_index.at(2);
+
+ if (in_range.contains(in_index))
+ {
+ out_index_k.at(3) = kernel_index.at(2) * channel_multiplier + kernel_index.at(3);
+ res_accessor.at(out_index_k) += input.at(in_index) * kernel.at(kernel_index);
+ }
+ }
+ }
+}
+
+template <> struct DepthwiseConv2DImpl<uint8_t>
+{
+ static void run(const mir::ops::DepthwiseConv2DOp &op, const mir::TensorVariant &inputv,
+ const mir::TensorVariant &kernelv, const mir::TensorVariant *biasv,
+ mir::TensorVariant &output);
+};
+
+void DepthwiseConv2DImpl<uint8_t>::run(const mir::ops::DepthwiseConv2DOp &op,
+ const mir::TensorVariant &inputv,
+ const mir::TensorVariant &kernelv,
+ const mir::TensorVariant *biasv, mir::TensorVariant &output)
+{
+ if (!biasv)
+ {
+ throw std::runtime_error{"Unsupported quantized DepthwiseConv2D without fused bias"};
+ }
+
+ const auto &input_type = inputv.getType();
+ const auto &kernel_type = kernelv.getType();
+ const auto &bias_type = biasv->getType();
+ const auto &output_type = op.getOutput(0)->getType();
+ (void)bias_type;
+
+ assert(input_type.isQuantized());
+ assert(kernel_type.isQuantized());
+ assert(bias_type.isQuantized());
+ assert(output_type.isQuantized());
+ assert(input_type.getElementType() == DataType::UINT8);
+ assert(kernel_type.getElementType() == DataType::UINT8);
+ assert(bias_type.getElementType() == DataType::INT32);
+
+ int32_t input_offset = -input_type.getQuantization().getZeroPoint();
+ int32_t kernel_offset = -kernel_type.getQuantization().getZeroPoint();
+ int32_t output_offset = output_type.getQuantization().getZeroPoint();
+
+ double input_scale = input_type.getQuantization().getScale();
+ double kernel_scale = kernel_type.getQuantization().getScale();
+ double output_scale = output_type.getQuantization().getScale();
+
+ double real_multiplier = input_scale * kernel_scale / output_scale;
+ int32_t output_multiplier = 0;
+ int output_shift = 0;
+ QuantizeMultiplier(real_multiplier, &output_multiplier, &output_shift);
+
+ const Shape &in_shape = inputv.getShape();
+ const Shape &kernel_shape = kernelv.getShape();
+ const Shape &out_shape = op.getOutputShape(0);
+ const auto &strides = op.getStrides();
+ const std::vector<int32_t> &pads = op.getPaddingBefore();
+
+ assert(in_shape.rank() == 4);
+ assert(kernel_shape.rank() == 4);
+ assert(kernel_shape.dim(2) == in_shape.dim(3)); // HWIO
+ assert(in_shape.dim(3) * kernel_shape.dim(3) == out_shape.dim(3));
+ assert(strides.size() == 2);
+ assert(pads.size() == 2);
+
+ int32_t stride_height = strides[0];
+ int32_t stride_width = strides[1];
+
+ int32_t pad_height = pads[0];
+ int32_t pad_width = pads[1];
+
+ int32_t input_height = in_shape.dim(1);
+ int32_t input_width = in_shape.dim(2);
+
+ Tensor<uint8_t> input_accessor(inputv);
+ Tensor<uint8_t> kernel_accessor(kernelv);
+ Tensor<int32_t> bias_accessor(*biasv);
+ Tensor<uint8_t> res_accessor(output);
+
+ int32_t output_min = std::numeric_limits<uint8_t>::min();
+ int32_t output_max = std::numeric_limits<uint8_t>::max();
+
+ int batches = out_shape.dim(0);
+ int output_height = out_shape.dim(1);
+ int output_width = out_shape.dim(2);
+ int input_depth = in_shape.dim(3);
+
+ int filter_height = kernel_shape.dim(0); // HWIO
+ int filter_width = kernel_shape.dim(1); // HWIO
+
+ for (int b = 0; b < batches; ++b)
+ {
+ for (int out_y = 0; out_y < output_height; ++out_y)
+ {
+ for (int out_x = 0; out_x < output_width; ++out_x)
+ {
+ for (int ic = 0; ic < input_depth; ++ic)
+ {
+ const int oc = ic;
+ const int in_x_origin = (out_x * stride_width) - pad_width;
+ const int in_y_origin = (out_y * stride_height) - pad_height;
+ int32_t acc = 0;
+ for (int filter_y = 0; filter_y < filter_height; ++filter_y)
+ {
+ for (int filter_x = 0; filter_x < filter_width; ++filter_x)
+ {
+ const int in_x = in_x_origin + filter_x;
+ const int in_y = in_y_origin + filter_y;
+ // If the location is outside the bounds of the input image,
+ // use zero as a default value.
+ if ((in_x >= 0) && (in_x < input_width) && (in_y >= 0) && (in_y < input_height))
+ {
+ Index in_index{b, in_y, in_x, ic};
+ Index ker_index{filter_y, filter_x, oc, 0}; // HWIO
+ int32_t input_val = input_accessor.at(in_index);
+ int32_t kernel_val = kernel_accessor.at(ker_index);
+ acc += (kernel_val + kernel_offset) * (input_val + input_offset);
+ }
+ }
+ }
+ acc += bias_accessor.at(Index{oc});
+ acc = MultiplyByQuantizedMultiplier(acc, output_multiplier, output_shift);
+ acc += output_offset;
+ acc = std::max(acc, output_min);
+ acc = std::min(acc, output_max);
+ Index out_index{b, out_y, out_x, oc};
+ res_accessor.at(out_index) = static_cast<uint8_t>(acc);
+ }
+ }
+ }
+ }
+}
+
+void DepthwiseConv2D(const mir::ops::DepthwiseConv2DOp &op, const mir::TensorVariant &input,
+ const mir::TensorVariant &kernel, mir::TensorVariant &output,
+ const mir::TensorVariant *bias)
+{
+ dispatch<DepthwiseConv2DImpl>(output.getElementType(), op, input, kernel, bias, output);
+}
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/DepthwiseConv2D.h b/compiler/mir-interpreter/src/ops/DepthwiseConv2D.h
new file mode 100644
index 000000000..d89529fc9
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/DepthwiseConv2D.h
@@ -0,0 +1,32 @@
+/*
+ * 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 _NNC_CORE_BACKEND_INTERPRETER_DEPTHWISE_CONV2D_IMPL_
+#define _NNC_CORE_BACKEND_INTERPRETER_DEPTHWISE_CONV2D_IMPL_
+
+#include "mir/ops/DepthwiseConv2DOp.h"
+#include "mir/TensorVariant.h"
+
+namespace mir_interpreter
+{
+
+void DepthwiseConv2D(const mir::ops::DepthwiseConv2DOp &op, const mir::TensorVariant &input,
+ const mir::TensorVariant &kernel, mir::TensorVariant &output,
+ const mir::TensorVariant *bias);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_DEPTHWISE_CONV2D_IMPL_
diff --git a/compiler/mir-interpreter/src/ops/Div.cpp b/compiler/mir-interpreter/src/ops/Div.cpp
new file mode 100644
index 000000000..00553e7e0
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Div.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2020 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 "Div.h"
+#include "Common.h"
+
+#include "mir/ShapeRange.h"
+#include "mir/Tensor.h"
+
+namespace mir_interpreter
+{
+
+using namespace mir;
+
+template <typename T> struct DivImpl
+{
+ static void run(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res);
+};
+
+template <typename T>
+void DivImpl<T>::run(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res)
+{
+ TensorVariant broadcasted_lhs(lhs, res.getShape());
+ TensorVariant broadcasted_rhs(rhs, res.getShape());
+ Tensor<T> lhs_accessor(broadcasted_lhs);
+ Tensor<T> rhs_accessor(broadcasted_rhs);
+ Tensor<T> res_accessor(res);
+
+ for (const auto &index : ShapeRange(res.getShape()))
+ {
+ res_accessor.at(index) = lhs_accessor.at(index) / rhs_accessor.at(index);
+ }
+}
+
+template <> struct DivImpl<uint8_t>
+{
+ static void run(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res)
+ {
+ // No support for quantized elementwise div yet
+ throw std::runtime_error{"NYI"};
+ }
+};
+
+void Div(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res)
+{
+ dispatch<DivImpl>(res.getElementType(), lhs, rhs, res);
+}
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/Div.h b/compiler/mir-interpreter/src/ops/Div.h
new file mode 100644
index 000000000..558e299ec
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Div.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 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 _NNC_CORE_BACKEND_INTERPRETER_DIV_
+#define _NNC_CORE_BACKEND_INTERPRETER_DIV_
+
+#include "mir/TensorVariant.h"
+
+namespace mir_interpreter
+{
+
+void Div(const mir::TensorVariant &lhs, const mir::TensorVariant &rhs, mir::TensorVariant &res);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_DIV_
diff --git a/compiler/mir-interpreter/src/ops/ELU.cpp b/compiler/mir-interpreter/src/ops/ELU.cpp
new file mode 100644
index 000000000..0cd76baf4
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/ELU.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2019 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 "ReLU.h"
+#include "Common.h"
+
+#include "mir/ShapeRange.h"
+#include "mir/Tensor.h"
+
+#include <cmath>
+
+namespace mir_interpreter
+{
+
+template <typename T> struct ELUImpl
+{
+ static void run(const mir::TensorVariant &arg, float alpha, mir::TensorVariant &result);
+};
+
+template <typename T>
+void ELUImpl<T>::run(const mir::TensorVariant &arg, float alpha, mir::TensorVariant &result)
+{
+ mir::Tensor<T> arg_accessor(arg);
+ mir::Tensor<T> res_accessor(result);
+
+ for (const auto &index : mir::ShapeRange(result.getShape()))
+ {
+ const T x = arg_accessor.at(index);
+ res_accessor.at(index) = x < 0 ? alpha * (std::exp(x) - 1) : x;
+ }
+}
+
+void ELU(const mir::TensorVariant &arg, float alpha, mir::TensorVariant &result)
+{
+ dispatch<ELUImpl>(result.getElementType(), arg, alpha, result);
+}
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/ELU.h b/compiler/mir-interpreter/src/ops/ELU.h
new file mode 100644
index 000000000..c6ebae1a7
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/ELU.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 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 _NNC_CORE_BACKEND_INTERPRETER_ELU_
+#define _NNC_CORE_BACKEND_INTERPRETER_ELU_
+
+#include <mir/TensorVariant.h>
+
+namespace mir_interpreter
+{
+
+void ELU(const mir::TensorVariant &arg, float alpha, mir::TensorVariant &result);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_ELU_
diff --git a/compiler/mir-interpreter/src/ops/Equal.cpp b/compiler/mir-interpreter/src/ops/Equal.cpp
new file mode 100644
index 000000000..b75ea5543
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Equal.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2020 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 "Equal.h"
+#include "Common.h"
+
+#include "mir/ShapeRange.h"
+#include "mir/Tensor.h"
+
+namespace mir_interpreter
+{
+
+using namespace mir;
+
+template <typename T> struct EqualImpl
+{
+ static void run(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res);
+};
+
+template <typename T>
+void EqualImpl<T>::run(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res)
+{
+ TensorVariant broadcasted_lhs(lhs, res.getShape());
+ TensorVariant broadcasted_rhs(rhs, res.getShape());
+ Tensor<T> lhs_accessor(broadcasted_lhs);
+ Tensor<T> rhs_accessor(broadcasted_rhs);
+ Tensor<uint8_t> res_accessor(res);
+
+ for (const auto &index : ShapeRange(res.getShape()))
+ {
+ res_accessor.at(index) = (lhs_accessor.at(index) == rhs_accessor.at(index));
+ }
+}
+
+void Equal(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res)
+{
+ if (lhs.getElementType() != rhs.getElementType())
+ {
+ throw std::runtime_error{"Equal with different input types is unsupported"};
+ }
+
+ dispatch<EqualImpl>(lhs.getElementType(), lhs, rhs, res);
+}
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/Equal.h b/compiler/mir-interpreter/src/ops/Equal.h
new file mode 100644
index 000000000..2d112a2f1
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Equal.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 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 _NNC_CORE_BACKEND_INTERPRETER_EQUAL_
+#define _NNC_CORE_BACKEND_INTERPRETER_EQUAL_
+
+#include "mir/TensorVariant.h"
+
+namespace mir_interpreter
+{
+
+void Equal(const mir::TensorVariant &lhs, const mir::TensorVariant &rhs, mir::TensorVariant &res);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_EQUAL_
diff --git a/compiler/mir-interpreter/src/ops/Fill.h b/compiler/mir-interpreter/src/ops/Fill.h
new file mode 100644
index 000000000..6dee25b8a
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Fill.h
@@ -0,0 +1,48 @@
+/*
+ * 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 _NNC_CORE_BACKEND_INTERPRETER_FILL_
+#define _NNC_CORE_BACKEND_INTERPRETER_FILL_
+
+#include "Common.h"
+
+#include "mir/ShapeRange.h"
+#include "mir/Tensor.h"
+
+namespace mir_interpreter
+{
+
+template <typename T> struct FillImpl
+{
+ template <typename F> static void run(mir::TensorVariant &res, F f)
+ {
+ mir::Tensor<T> res_accessor(res);
+
+ for (const auto &index : mir::ShapeRange(res.getShape()))
+ {
+ res_accessor.at(index) = f(index);
+ }
+ }
+};
+
+template <typename F> void Fill(mir::TensorVariant &t, F f)
+{
+ dispatch<FillImpl>(t.getElementType(), t, f);
+}
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_FILL_
diff --git a/compiler/mir-interpreter/src/ops/FullyConnected.cpp b/compiler/mir-interpreter/src/ops/FullyConnected.cpp
new file mode 100644
index 000000000..9c6ef8dc8
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/FullyConnected.cpp
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2019 The TensorFlow Authors. 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 "FullyConnected.h"
+#include "Common.h"
+
+#include "QuantizationHelpers.h"
+
+#include "mir/Tensor.h"
+
+namespace mir_interpreter
+{
+
+template <typename T>
+static void fullyConnected2D(const mir::TensorVariant &input, const mir::TensorVariant &weights,
+ mir::TensorVariant &output)
+{
+ assert(input.getShape().rank() == 2);
+ assert(weights.getShape().rank() == 2);
+ assert(input.getShape().dim(1) == weights.getShape().dim(0));
+
+ auto in_raw = reinterpret_cast<T *>(input.atOffset(0));
+ auto weight_raw = reinterpret_cast<T *>(weights.atOffset(0));
+ auto output_raw = reinterpret_cast<T *>(output.atOffset(0));
+
+ auto rows = output.getShape().dim(0);
+ auto cols = output.getShape().dim(1);
+ auto N = input.getShape().dim(1);
+ auto wcols = weights.getShape().dim(1);
+
+ for (int32_t r = 0; r < rows; ++r)
+ {
+ for (int32_t k = 0; k < N; ++k)
+ {
+ auto in = in_raw[r * N + k];
+
+ for (int32_t c = 0; c < cols; ++c)
+ {
+ output_raw[r * cols + c] += in * weight_raw[k * wcols + c];
+ }
+ }
+ }
+}
+
+template <typename T> struct FullyConnectedImpl
+{
+ static void run(const mir::TensorVariant &inputv, const mir::TensorVariant &weightsv,
+ const mir::ops::FullyConnectedOp &op, mir::TensorVariant &res,
+ const mir::TensorVariant *biasv);
+};
+
+template <typename T>
+void FullyConnectedImpl<T>::run(const mir::TensorVariant &inputv,
+ const mir::TensorVariant &weightsv,
+ const mir::ops::FullyConnectedOp &op, mir::TensorVariant &res,
+ const mir::TensorVariant *biasv)
+{
+ if (biasv)
+ {
+ throw std::runtime_error("non-quantized FullyConnected with fused bias is unsupported");
+ }
+
+ mir::Tensor<T> input{inputv};
+ mir::Tensor<T> weights{weightsv};
+
+ erase<T>(res);
+
+ if (input.getShape().rank() == 2 && weights.getShape().rank() == 2 && res.getShape().rank() == 2)
+ {
+ // optimized case for 2d matrix multiplication
+ fullyConnected2D<T>(inputv, weightsv, res);
+ return;
+ }
+
+ mir::Tensor<T> accessor(res);
+
+ const mir::Shape &in_shape = input.getShape();
+ int32_t in_rank = in_shape.rank();
+
+ const mir::Shape &w_shape = weights.getShape();
+ int32_t w_rank = w_shape.rank();
+
+ assert(in_shape.dim(in_rank - 1) == w_shape.dim(w_rank - 2));
+ (void)in_rank;
+
+ mir::ShapeRange out_range(res.getShape());
+
+ int32_t len = w_shape.dim(w_rank - 2);
+
+ for (auto &out_index : out_range)
+ {
+ mir::Index t_index = out_index;
+ T &output_element = accessor.at(out_index);
+ int32_t col = t_index.at(w_rank - 1);
+ int32_t row = t_index.at(w_rank - 2);
+ for (int32_t i = 0; i < len; ++i)
+ {
+ t_index.at(w_rank - 1) = i;
+ T in = input.at(t_index);
+ t_index.at(w_rank - 1) = col;
+ t_index.at(w_rank - 2) = i;
+ T w = weights.at(t_index);
+ t_index.at(w_rank - 2) = row;
+ output_element += in * w;
+ }
+ }
+}
+
+template <> struct FullyConnectedImpl<uint8_t>
+{
+ static void run(const mir::TensorVariant &inputv, const mir::TensorVariant &weightsv,
+ const mir::ops::FullyConnectedOp &op, mir::TensorVariant &res,
+ const mir::TensorVariant *biasv);
+};
+
+void FullyConnectedImpl<uint8_t>::run(const mir::TensorVariant &inputv,
+ const mir::TensorVariant &weightsv,
+ const mir::ops::FullyConnectedOp &op, mir::TensorVariant &res,
+ const mir::TensorVariant *biasv)
+{
+ if (!biasv)
+ {
+ throw std::runtime_error{"Quantized FullyConnected cannot be executed without fused bias"};
+ }
+
+ const auto &input_type = inputv.getType();
+ const auto &weights_type = weightsv.getType();
+ const auto &bias_type = biasv->getType();
+ const auto &output_type = op.getOutput(0)->getType();
+ (void)bias_type;
+
+ assert(input_type.isQuantized());
+ assert(weights_type.isQuantized());
+ assert(bias_type.isQuantized());
+ assert(output_type.isQuantized());
+ assert(input_type.getElementType() == mir::DataType::UINT8);
+ assert(weights_type.getElementType() == mir::DataType::UINT8);
+ assert(bias_type.getElementType() == mir::DataType::INT32);
+
+ int32_t input_offset = -input_type.getQuantization().getZeroPoint();
+ int32_t weights_offset = -weights_type.getQuantization().getZeroPoint();
+ int32_t output_offset = output_type.getQuantization().getZeroPoint();
+
+ double input_scale = input_type.getQuantization().getScale();
+ double weights_scale = weights_type.getQuantization().getScale();
+ double output_scale = output_type.getQuantization().getScale();
+
+ double real_multiplier = input_scale * weights_scale / output_scale;
+ int32_t output_multiplier = 0;
+ int output_shift = 0;
+ QuantizeMultiplier(real_multiplier, &output_multiplier, &output_shift);
+
+ const mir::Shape &in_shape = inputv.getShape();
+ const mir::Shape &weights_shape = weightsv.getShape();
+ const mir::Shape &out_shape = op.getOutputShape(0);
+
+ const int32_t batches = in_shape.dim(0);
+ assert(in_shape.rank() == 2);
+ assert(weights_shape.rank() == 2);
+ assert(in_shape.dim(1) == weights_shape.dim(0));
+ const int32_t accum_depth = weights_shape.dim(0);
+ const int32_t output_depth = weights_shape.dim(1);
+
+ uint8_t *input_data = reinterpret_cast<uint8_t *>(inputv.atOffset(0));
+ uint8_t *weights_data = reinterpret_cast<uint8_t *>(weightsv.atOffset(0));
+ int32_t *bias_data = reinterpret_cast<int32_t *>(biasv->atOffset(0));
+
+ uint8_t *output_data = reinterpret_cast<uint8_t *>(res.atOffset(0));
+
+ int32_t output_min = std::numeric_limits<uint8_t>::min();
+ int32_t output_max = std::numeric_limits<uint8_t>::max();
+
+ for (int32_t b = 0; b < batches; ++b)
+ {
+ for (int32_t out_c = 0; out_c < output_depth; ++out_c)
+ {
+ int32_t acc = 0;
+ for (int d = 0; d < accum_depth; ++d)
+ {
+ int32_t input_val = input_data[b * accum_depth + d];
+ int32_t weights_val = weights_data[d * output_depth + out_c];
+ acc += (weights_val + weights_offset) * (input_val + input_offset);
+ }
+ acc += bias_data[out_c];
+ acc = MultiplyByQuantizedMultiplier(acc, output_multiplier, output_shift);
+ acc += output_offset;
+ acc = std::max(acc, output_min);
+ acc = std::min(acc, output_max);
+ output_data[out_c + output_depth * b] = static_cast<uint8_t>(acc);
+ }
+ }
+}
+
+void FullyConnected(const mir::TensorVariant &input, const mir::TensorVariant &weights,
+ const mir::ops::FullyConnectedOp &op, mir::TensorVariant &res,
+ const mir::TensorVariant *bias)
+{
+ dispatch<FullyConnectedImpl>(res.getElementType(), input, weights, op, res, bias);
+}
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/FullyConnected.h b/compiler/mir-interpreter/src/ops/FullyConnected.h
new file mode 100644
index 000000000..fdfe64265
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/FullyConnected.h
@@ -0,0 +1,32 @@
+/*
+ * 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 _NNC_CORE_BACKEND_INTERPRETER_FULLYCONNECTED_
+#define _NNC_CORE_BACKEND_INTERPRETER_FULLYCONNECTED_
+
+#include "mir/ops/FullyConnectedOp.h"
+#include "mir/ShapeRange.h"
+
+namespace mir_interpreter
+{
+
+void FullyConnected(const mir::TensorVariant &input, const mir::TensorVariant &weights,
+ const mir::ops::FullyConnectedOp &op, mir::TensorVariant &res,
+ const mir::TensorVariant *bias);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_FULLYCONNECTED_
diff --git a/compiler/mir-interpreter/src/ops/Gather.cpp b/compiler/mir-interpreter/src/ops/Gather.cpp
new file mode 100644
index 000000000..4328c26b2
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Gather.cpp
@@ -0,0 +1,92 @@
+/*
+ * 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 "Gather.h"
+#include "Common.h"
+
+#include "mir/Tensor.h"
+
+namespace mir_interpreter
+{
+
+using namespace mir;
+
+template <typename T, typename IndicesT> struct GatherImpl
+{
+ static void run(const TensorVariant &datav, const TensorVariant &indicesv,
+ const ops::GatherOp &op, mir::TensorVariant &res);
+};
+
+template <typename T, typename IndicesT>
+void GatherImpl<T, IndicesT>::run(const TensorVariant &datav, const TensorVariant &indicesv,
+ const ops::GatherOp &op, TensorVariant &res)
+{
+ const auto &data_shape = datav.getShape();
+ const auto &indices_shape = indicesv.getShape();
+ Tensor<T> data(datav);
+ Tensor<T> output(res);
+ Tensor<IndicesT> indices(indicesv);
+
+ int32_t axis = op.getAxis();
+ if (axis < 0)
+ axis += data_shape.rank();
+ assert(axis >= 0 && axis < data_shape.rank());
+ int32_t axis_size = data_shape.dim(axis);
+ int32_t num_indices = indices_shape.numElements();
+
+ int32_t outer_size = 1;
+ for (int32_t i = 0; i < axis; ++i)
+ outer_size *= data_shape.dim(i);
+
+ int32_t inner_size = 1;
+ for (int32_t i = axis + 1; i < data_shape.rank(); ++i)
+ inner_size *= data_shape.dim(i);
+
+ for (int32_t outer = 0; outer < outer_size; ++outer)
+ {
+ for (int32_t i = 0; i < num_indices; ++i)
+ {
+ auto index = indices.atOffset(i);
+ assert(index >= 0 && index < axis_size);
+ for (int32_t inner = 0; inner < inner_size; inner++)
+ {
+ output.atOffset((outer * num_indices + i) * inner_size + inner) =
+ data.atOffset((outer * axis_size + index) * inner_size + inner);
+ }
+ }
+ }
+}
+
+// a hack to reuse dispath function
+template <typename T> struct GatherByT
+{
+
+ template <typename IndicesT> using GatherWithFixedT = GatherImpl<T, IndicesT>;
+
+ static void run(const TensorVariant &data, const TensorVariant &indices, const ops::GatherOp &op,
+ TensorVariant &res)
+ {
+ dispatch<GatherWithFixedT>(indices.getElementType(), data, indices, op, res);
+ }
+};
+
+void Gather(const TensorVariant &data, const TensorVariant &indices, const ops::GatherOp &op,
+ TensorVariant &res)
+{
+ dispatch<GatherByT>(data.getElementType(), data, indices, op, res);
+}
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/Gather.h b/compiler/mir-interpreter/src/ops/Gather.h
new file mode 100644
index 000000000..0f9648323
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Gather.h
@@ -0,0 +1,31 @@
+/*
+ * 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 _NNC_CORE_BACKEND_INTERPRETER_GATHER_
+#define _NNC_CORE_BACKEND_INTERPRETER_GATHER_
+
+#include "mir/ops/GatherOp.h"
+#include "mir/TensorVariant.h"
+
+namespace mir_interpreter
+{
+
+void Gather(const mir::TensorVariant &data, const mir::TensorVariant &indices,
+ const mir::ops::GatherOp &op, mir::TensorVariant &res);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_GATHER_
diff --git a/compiler/mir-interpreter/src/ops/Greater.cpp b/compiler/mir-interpreter/src/ops/Greater.cpp
new file mode 100644
index 000000000..36400292f
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Greater.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2020 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 "Greater.h"
+#include "Common.h"
+
+#include "mir/ShapeRange.h"
+#include "mir/Tensor.h"
+
+namespace mir_interpreter
+{
+
+using namespace mir;
+
+template <typename T> struct GreaterImpl
+{
+ static void run(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res);
+};
+
+template <typename T>
+void GreaterImpl<T>::run(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res)
+{
+ TensorVariant broadcasted_lhs(lhs, res.getShape());
+ TensorVariant broadcasted_rhs(rhs, res.getShape());
+ Tensor<T> lhs_accessor(broadcasted_lhs);
+ Tensor<T> rhs_accessor(broadcasted_rhs);
+ Tensor<uint8_t> res_accessor(res);
+
+ for (const auto &index : ShapeRange(res.getShape()))
+ {
+ res_accessor.at(index) = (lhs_accessor.at(index) > rhs_accessor.at(index));
+ }
+}
+
+void Greater(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res)
+{
+ if (lhs.getElementType() != rhs.getElementType())
+ {
+ throw std::runtime_error{"Greater with different input types is unsupported"};
+ }
+ dispatch<GreaterImpl>(lhs.getElementType(), lhs, rhs, res);
+}
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/Greater.h b/compiler/mir-interpreter/src/ops/Greater.h
new file mode 100644
index 000000000..812245ecd
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Greater.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 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 _NNC_CORE_BACKEND_INTERPRETER_GREATER_
+#define _NNC_CORE_BACKEND_INTERPRETER_GREATER_
+
+#include "mir/TensorVariant.h"
+
+namespace mir_interpreter
+{
+
+void Greater(const mir::TensorVariant &lhs, const mir::TensorVariant &rhs, mir::TensorVariant &res);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_GREATER_
diff --git a/compiler/mir-interpreter/src/ops/HardSwish.cpp b/compiler/mir-interpreter/src/ops/HardSwish.cpp
new file mode 100644
index 000000000..20f7820c2
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/HardSwish.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2020 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 "HardSwish.h"
+#include "Common.h"
+
+namespace mir_interpreter
+{
+
+template <typename T> struct HardSwishImpl
+{
+ static void run(const mir::TensorVariant &input, mir::TensorVariant &result);
+};
+
+template <typename T>
+void HardSwishImpl<T>::run(const mir::TensorVariant &input, mir::TensorVariant &result)
+{
+ auto output_data = reinterpret_cast<T *>(result.atOffset(0));
+ auto input_data = reinterpret_cast<T *>(input.atOffset(0));
+ auto in_end = input_data + input.getShape().numElements();
+ for (; input_data < in_end; input_data++, output_data++)
+ {
+ const auto in = *input_data;
+ *output_data = in * std::min<T>(6.f, std::max<T>(0.f, in + 3.f)) / 6.f;
+ }
+}
+
+template <> struct HardSwishImpl<uint8_t>
+{
+ static void run(const mir::TensorVariant &input, mir::TensorVariant &result)
+ {
+ throw std::runtime_error{"NYI"};
+ }
+};
+
+void HardSwish(const mir::TensorVariant &input, mir::TensorVariant &result)
+{
+ dispatch<HardSwishImpl>(input.getElementType(), input, result);
+}
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/HardSwish.h b/compiler/mir-interpreter/src/ops/HardSwish.h
new file mode 100644
index 000000000..9b39bb164
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/HardSwish.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 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 _NNC_CORE_BACKEND_INTERPRETER_HARDSWISH_IMPL_
+#define _NNC_CORE_BACKEND_INTERPRETER_HARDSWISH_IMPL_
+
+#include "mir/TensorVariant.h"
+
+namespace mir_interpreter
+{
+
+void HardSwish(const mir::TensorVariant &input, mir::TensorVariant &result);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_HARDSWISH_IMPL_
diff --git a/compiler/mir-interpreter/src/ops/LeakyReLU.cpp b/compiler/mir-interpreter/src/ops/LeakyReLU.cpp
new file mode 100644
index 000000000..5b265f9f5
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/LeakyReLU.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2019 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 "ReLU.h"
+#include "Common.h"
+
+#include <mir/ShapeRange.h>
+#include <mir/Tensor.h>
+
+namespace mir_interpreter
+{
+
+template <typename T> struct LeakyReLUImpl
+{
+ static void run(const mir::TensorVariant &arg, float alpha, mir::TensorVariant &result);
+};
+
+template <typename T>
+void LeakyReLUImpl<T>::run(const mir::TensorVariant &arg, float alpha, mir::TensorVariant &result)
+{
+ mir::Tensor<T> arg_accessor(arg);
+ mir::Tensor<T> res_accessor(result);
+
+ for (const auto &index : mir::ShapeRange(result.getShape()))
+ {
+ auto x = arg_accessor.at(index);
+ res_accessor.at(index) = x < 0 ? x * alpha : x;
+ }
+}
+
+void LeakyReLU(const mir::TensorVariant &arg, float alpha, mir::TensorVariant &result)
+{
+ dispatch<LeakyReLUImpl>(result.getElementType(), arg, alpha, result);
+}
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/LeakyReLU.h b/compiler/mir-interpreter/src/ops/LeakyReLU.h
new file mode 100644
index 000000000..6bf9b78ac
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/LeakyReLU.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 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 _NNC_CORE_BACKEND_INTERPRETER_LEAKYRELU_
+#define _NNC_CORE_BACKEND_INTERPRETER_LEAKYRELU_
+
+#include <mir/TensorVariant.h>
+
+namespace mir_interpreter
+{
+
+void LeakyReLU(const mir::TensorVariant &arg, float cap, mir::TensorVariant &result);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_LEAKYRELU_
diff --git a/compiler/mir-interpreter/src/ops/Less.cpp b/compiler/mir-interpreter/src/ops/Less.cpp
new file mode 100644
index 000000000..8da351915
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Less.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2020 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 "Less.h"
+#include "Common.h"
+
+#include "mir/ShapeRange.h"
+#include "mir/Tensor.h"
+
+namespace mir_interpreter
+{
+
+using namespace mir;
+
+template <typename T> struct LessImpl
+{
+ static void run(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res);
+};
+
+template <typename T>
+void LessImpl<T>::run(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res)
+{
+ TensorVariant broadcasted_lhs(lhs, res.getShape());
+ TensorVariant broadcasted_rhs(rhs, res.getShape());
+ Tensor<T> lhs_accessor(broadcasted_lhs);
+ Tensor<T> rhs_accessor(broadcasted_rhs);
+ Tensor<uint8_t> res_accessor(res);
+
+ for (const auto &index : ShapeRange(res.getShape()))
+ {
+ res_accessor.at(index) = (lhs_accessor.at(index) < rhs_accessor.at(index));
+ }
+}
+
+void Less(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res)
+{
+ if (lhs.getElementType() != rhs.getElementType())
+ {
+ throw std::runtime_error{"Less with different input types is unsupported"};
+ }
+ dispatch<LessImpl>(lhs.getElementType(), lhs, rhs, res);
+}
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/Less.h b/compiler/mir-interpreter/src/ops/Less.h
new file mode 100644
index 000000000..fa3edd2d0
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Less.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 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 _NNC_CORE_BACKEND_INTERPRETER_LESS_
+#define _NNC_CORE_BACKEND_INTERPRETER_LESS_
+
+#include "mir/TensorVariant.h"
+
+namespace mir_interpreter
+{
+
+void Less(const mir::TensorVariant &lhs, const mir::TensorVariant &rhs, mir::TensorVariant &res);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_LESS_
diff --git a/compiler/mir-interpreter/src/ops/Max.cpp b/compiler/mir-interpreter/src/ops/Max.cpp
new file mode 100644
index 000000000..eb284c77c
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Max.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2020 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 "Max.h"
+#include "Common.h"
+
+#include "mir/ShapeRange.h"
+#include "mir/Tensor.h"
+
+#include <algorithm>
+
+namespace mir_interpreter
+{
+
+using namespace mir;
+
+template <typename T> struct MaxImpl
+{
+ static void run(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res);
+};
+
+template <typename T>
+void MaxImpl<T>::run(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res)
+{
+ TensorVariant broadcasted_lhs(lhs, res.getShape());
+ TensorVariant broadcasted_rhs(rhs, res.getShape());
+ Tensor<T> lhs_accessor(broadcasted_lhs);
+ Tensor<T> rhs_accessor(broadcasted_rhs);
+ Tensor<T> res_accessor(res);
+
+ for (const auto &index : ShapeRange(res.getShape()))
+ {
+ res_accessor.at(index) = std::max(lhs_accessor.at(index), rhs_accessor.at(index));
+ }
+}
+template <> struct MaxImpl<uint8_t>
+{
+ static void run(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res)
+ {
+ throw std::runtime_error{"NYI"};
+ };
+};
+
+void Max(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res)
+{
+ if (lhs.getElementType() != rhs.getElementType())
+ {
+ throw std::runtime_error{"Max with different input types is unsupported"};
+ }
+ dispatch<MaxImpl>(lhs.getElementType(), lhs, rhs, res);
+}
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/Max.h b/compiler/mir-interpreter/src/ops/Max.h
new file mode 100644
index 000000000..b49d0602d
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Max.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 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 _NNC_CORE_BACKEND_INTERPRETER_MAX_
+#define _NNC_CORE_BACKEND_INTERPRETER_MAX_
+
+#include "mir/TensorVariant.h"
+
+namespace mir_interpreter
+{
+
+void Max(const mir::TensorVariant &lhs, const mir::TensorVariant &rhs, mir::TensorVariant &res);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_MAX_
diff --git a/compiler/mir-interpreter/src/ops/MaxPool2D.cpp b/compiler/mir-interpreter/src/ops/MaxPool2D.cpp
new file mode 100644
index 000000000..cec2f5984
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/MaxPool2D.cpp
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2019 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 "MaxPool2D.h"
+#include "Common.h"
+
+#include "mir/ShapeRange.h"
+#include "mir/Tensor.h"
+
+#include <limits>
+
+namespace mir_interpreter
+{
+
+using namespace mir;
+
+template <typename T> struct MaxPool2DImpl
+{
+ static void run(const mir::TensorVariant &inputv, const mir::ops::MaxPool2DOp &op,
+ mir::TensorVariant &result);
+};
+
+template <typename T>
+void MaxPool2DImpl<T>::run(const TensorVariant &inputv, const ops::MaxPool2DOp &op,
+ TensorVariant &result)
+{
+ const auto &input_shape = op.getInputShape(0);
+ const auto &output_shape = op.getOutputShape(0);
+ const auto &window_size = op.getWindowSize();
+ const auto &strides = op.getStrides();
+ const auto &padding_before = op.getPaddingBefore();
+ const auto &padding_after = op.getPaddingAfter();
+ (void)padding_after;
+
+ Tensor<T> input(inputv);
+
+ constexpr int num_spatial_dims = 2;
+ assert(input.getShape().rank() == 4);
+ assert(window_size.size() == num_spatial_dims);
+ assert(strides.size() == num_spatial_dims);
+ assert(padding_before.size() == num_spatial_dims);
+ assert(padding_after.size() == num_spatial_dims);
+
+ Tensor<T> res_accessor(result);
+
+ ShapeRange in_range(input_shape);
+ Index in_index(input_shape.rank());
+
+ for (const auto &out_index : ShapeRange(output_shape))
+ {
+ T result = std::numeric_limits<T>::lowest();
+
+ // Assuming NHWC format.
+ in_index.at(0) = out_index.at(0);
+ in_index.at(3) = out_index.at(3);
+
+ for (const auto &window_index : ShapeRange(Shape(window_size)))
+ {
+ // Assuming NHWC format.
+ for (int i = 0; i < num_spatial_dims; ++i)
+ in_index.at(1 + i) =
+ out_index.at(1 + i) * strides[i] + window_index.at(i) - padding_before[i];
+
+ if (in_range.contains(in_index))
+ {
+ result = std::max(result, input.at(in_index));
+ }
+ }
+
+ res_accessor.at(out_index) = result;
+ }
+}
+
+template <> struct MaxPool2DImpl<uint8_t>
+{
+ static void run(const mir::TensorVariant &input, const mir::ops::MaxPool2DOp &op,
+ mir::TensorVariant &result);
+};
+
+void MaxPool2DImpl<uint8_t>::run(const TensorVariant &input, const ops::MaxPool2DOp &op,
+ TensorVariant &result)
+{
+ const auto &input_type = input.getType();
+ const auto &output_type = op.getOutput(0)->getType();
+ (void)input_type;
+
+ assert(input_type.isQuantized());
+ assert(output_type.isQuantized());
+ assert(input_type.getElementType() == DataType::UINT8);
+
+ const auto &input_shape = op.getInputShape(0);
+ const auto &output_shape = op.getOutputShape(0);
+ const auto &window_size = op.getWindowSize();
+ const auto &strides = op.getStrides();
+ const auto &padding_before = op.getPaddingBefore();
+ const auto &padding_after = op.getPaddingAfter();
+ (void)padding_after;
+
+ constexpr int num_spatial_dims = 2;
+ assert(input.getShape().rank() == 4);
+ assert(window_size.size() == num_spatial_dims);
+ assert(strides.size() == num_spatial_dims);
+ assert(padding_before.size() == num_spatial_dims);
+ assert(padding_after.size() == num_spatial_dims);
+
+ Tensor<uint8_t> input_accessor(input);
+
+ TensorType res_type(mir::DataType::UINT8, output_shape, output_type.getQuantization());
+ TensorVariant res(res_type);
+ Tensor<uint8_t> res_accessor(res);
+
+ ShapeRange in_range(input_shape);
+ Index in_index(input_shape.rank());
+
+ for (const auto &out_index : ShapeRange(output_shape))
+ {
+ // Assuming NHWC format.
+ in_index.at(0) = out_index.at(0);
+ in_index.at(3) = out_index.at(3);
+
+ uint8_t result = 0;
+ for (const auto &window_index : ShapeRange(Shape(window_size)))
+ {
+ // Assuming NHWC format.
+ for (int i = 0; i < num_spatial_dims; ++i)
+ in_index.at(1 + i) =
+ out_index.at(1 + i) * strides[i] + window_index.at(i) - padding_before[i];
+
+ if (in_range.contains(in_index))
+ {
+ result = std::max(result, input_accessor.at(in_index));
+ }
+ }
+ res_accessor.at(out_index) = result;
+ }
+}
+
+void MaxPool2D(const mir::TensorVariant &input, const mir::ops::MaxPool2DOp &op,
+ mir::TensorVariant &result)
+{
+ dispatch<MaxPool2DImpl>(input.getElementType(), input, op, result);
+};
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/MaxPool2D.h b/compiler/mir-interpreter/src/ops/MaxPool2D.h
new file mode 100644
index 000000000..564def207
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/MaxPool2D.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2019 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 _NNC_CORE_BACKEND_INTERPRETER_MAX_POOL_2D_
+#define _NNC_CORE_BACKEND_INTERPRETER_MAX_POOL_2D_
+
+#include "mir/ops/MaxPool2DOp.h"
+#include "mir/TensorVariant.h"
+
+namespace mir_interpreter
+{
+
+void MaxPool2D(const mir::TensorVariant &input, const mir::ops::MaxPool2DOp &op,
+ mir::TensorVariant &result);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_MAX_POOL_2D_
diff --git a/compiler/mir-interpreter/src/ops/Mul.cpp b/compiler/mir-interpreter/src/ops/Mul.cpp
new file mode 100644
index 000000000..446577c58
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Mul.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2020 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 "Mul.h"
+#include "Common.h"
+
+#include "mir/ShapeRange.h"
+#include "mir/Tensor.h"
+
+namespace mir_interpreter
+{
+
+using namespace mir;
+
+template <typename T> struct MulImpl
+{
+ static void run(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res);
+};
+
+template <typename T>
+void MulImpl<T>::run(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res)
+{
+ TensorVariant broadcasted_lhs(lhs, res.getShape());
+ TensorVariant broadcasted_rhs(rhs, res.getShape());
+ Tensor<T> lhs_accessor(broadcasted_lhs);
+ Tensor<T> rhs_accessor(broadcasted_rhs);
+ Tensor<T> res_accessor(res);
+
+ for (const auto &index : ShapeRange(res.getShape()))
+ {
+ res_accessor.at(index) = lhs_accessor.at(index) * rhs_accessor.at(index);
+ }
+}
+
+template <> struct MulImpl<uint8_t>
+{
+ static void run(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res)
+ {
+ throw std::runtime_error{"NYI"};
+ }
+};
+
+void Mul(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res)
+{
+ dispatch<MulImpl>(lhs.getElementType(), lhs, rhs, res);
+};
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/Mul.h b/compiler/mir-interpreter/src/ops/Mul.h
new file mode 100644
index 000000000..b2e71fa85
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Mul.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 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 _NNC_CORE_BACKEND_INTERPRETER_MUL_
+#define _NNC_CORE_BACKEND_INTERPRETER_MUL_
+
+#include "mir/TensorVariant.h"
+
+namespace mir_interpreter
+{
+
+void Mul(const mir::TensorVariant &lhs, const mir::TensorVariant &rhs, mir::TensorVariant &res);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_MUL_
diff --git a/compiler/mir-interpreter/src/ops/Pad.cpp b/compiler/mir-interpreter/src/ops/Pad.cpp
new file mode 100644
index 000000000..054a1b68a
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Pad.cpp
@@ -0,0 +1,84 @@
+/*
+ * 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 "Pad.h"
+#include "Common.h"
+
+#include "mir/ShapeRange.h"
+#include "mir/Tensor.h"
+
+namespace mir_interpreter
+{
+
+using namespace mir;
+
+template <typename T> struct PadImpl
+{
+ static void run(const mir::TensorVariant &inputv, const mir::ops::PadOp &op,
+ mir::TensorVariant &result);
+};
+
+template <typename T>
+void PadImpl<T>::run(const TensorVariant &inputv, const ops::PadOp &op, TensorVariant &result)
+{
+ Tensor<T> result_accessor(result);
+ Tensor<T> input(inputv);
+
+ Shape out_shape = result_accessor.getShape();
+
+ ShapeRange out_range(out_shape);
+ const int rank = op.getInputShape(0).rank();
+ const auto &padding_before = op.getPaddingBefore();
+ const auto &padding_after = op.getPaddingAfter();
+
+ Index temp_index;
+ temp_index.resize(rank);
+
+ bool index_on_padding(false);
+ for (const Index &ind : out_range)
+ {
+ index_on_padding = false;
+
+ for (int32_t i = 0; i < rank; i++)
+ {
+ // index on input values
+ if (ind.at(i) >= padding_before[i] && ind.at(i) < out_shape.dim(i) - padding_after[i])
+ {
+ temp_index.at(i) = ind.at(i) - padding_before[i];
+ }
+ else
+ { // not in input
+ index_on_padding = true;
+ break;
+ }
+ }
+ if (index_on_padding)
+ {
+ result_accessor.at(ind) = op.getPaddingValue();
+ }
+ else
+ {
+ result_accessor.at(ind) = input.at(temp_index);
+ }
+ }
+}
+
+void Pad(const mir::TensorVariant &input, const mir::ops::PadOp &op, mir::TensorVariant &result)
+{
+ dispatch<PadImpl>(input.getElementType(), input, op, result);
+};
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/Pad.h b/compiler/mir-interpreter/src/ops/Pad.h
new file mode 100644
index 000000000..cd72b8afd
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Pad.h
@@ -0,0 +1,36 @@
+/*
+ * 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 _NNC_CORE_BACKEND_INTERPRETER_PAD_IMPL_
+#define _NNC_CORE_BACKEND_INTERPRETER_PAD_IMPL_
+
+#include "mir/ops/PadOp.h"
+#include "mir/TensorVariant.h"
+
+namespace mir_interpreter
+{
+/**
+ * @brief Implements PadOp for interpreter backend
+ *
+ * This operation pads a tensor according to the paddings
+ * you specify. For each dimension of input add values
+ * before and after of contents.
+ */
+void Pad(const mir::TensorVariant &input, const mir::ops::PadOp &op, mir::TensorVariant &result);
+
+} // namespace mir_interpreter
+
+#endif // _NNC_CORE_BACKEND_INTERPRETER_PAD_IMPL_
diff --git a/compiler/mir-interpreter/src/ops/Quantization.cpp b/compiler/mir-interpreter/src/ops/Quantization.cpp
new file mode 100644
index 000000000..283a7c751
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Quantization.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2020 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 "Quantization.h"
+#include "mir/Tensor.h"
+#include "mir/ShapeRange.h"
+
+#include <cmath>
+#include <limits>
+
+namespace mir_interpreter
+{
+using namespace mir;
+
+void Dequantize(const TensorVariant &input, TensorVariant &output)
+{
+ const TensorType &input_type = input.getType();
+ assert(input_type.isQuantized());
+ assert(input_type.getElementType() == DataType::UINT8);
+
+ const float scale = input_type.getQuantization().getScale();
+ const int32_t zero_point = input_type.getQuantization().getZeroPoint();
+
+ Tensor<uint8_t> input_accessor(input);
+ Tensor<float> res_accessor(output);
+
+ for (const auto &index : ShapeRange(output.getShape()))
+ {
+ const int32_t value = input_accessor.at(index);
+ res_accessor.at(index) = scale * static_cast<float>(value - zero_point);
+ }
+}
+
+void Quantize(const TensorVariant &input, TensorVariant &output)
+{
+ const TensorType &output_type = output.getType();
+ assert(output_type.isQuantized());
+ assert(input.getElementType() == DataType::FLOAT32);
+
+ const float scale = output_type.getQuantization().getScale();
+ const int32_t zero_point = output_type.getQuantization().getZeroPoint();
+
+ const int32_t min_val = std::numeric_limits<uint8_t>::min();
+ const int32_t max_val = std::numeric_limits<uint8_t>::max();
+
+ Tensor<float> input_accessor(input);
+ Tensor<uint8_t> res_accessor(output);
+
+ for (const auto &index : ShapeRange(output.getShape()))
+ {
+ const float value = input_accessor.at(index);
+ int32_t unclamped = static_cast<int32_t>(std::round(value / scale)) + zero_point;
+ int32_t clamped = std::min(std::max(unclamped, min_val), max_val);
+ res_accessor.at(index) = static_cast<uint8_t>(clamped);
+ }
+}
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/Quantization.h b/compiler/mir-interpreter/src/ops/Quantization.h
new file mode 100644
index 000000000..23388d4d8
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Quantization.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2019 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 _NNC_CORE_BACKEND_INTERPRETER_QUANTIZATION_IMPL_
+#define _NNC_CORE_BACKEND_INTERPRETER_QUANTIZATION_IMPL_
+
+#include "mir/TensorVariant.h"
+
+namespace mir_interpreter
+{
+using namespace mir;
+
+void Dequantize(const TensorVariant &input, TensorVariant &output);
+
+void Quantize(const TensorVariant &input, TensorVariant &output);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_QUANTIZATION_IMPL_
diff --git a/compiler/mir-interpreter/src/ops/QuantizationHelpers.h b/compiler/mir-interpreter/src/ops/QuantizationHelpers.h
new file mode 100644
index 000000000..8faeffbd3
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/QuantizationHelpers.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2019 The TensorFlow Authors. 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 _NNC_CORE_BACKEND_INTERPRETER_QUANTIZATION_HELPERS_
+#define _NNC_CORE_BACKEND_INTERPRETER_QUANTIZATION_HELPERS_
+
+#include <cmath>
+#include <limits>
+
+namespace mir_interpreter
+{
+
+inline void QuantizeMultiplier(double double_multiplier, int32_t *quantized_multiplier, int *shift)
+{
+ if (double_multiplier == 0.)
+ {
+ *quantized_multiplier = 0;
+ *shift = 0;
+ return;
+ }
+
+ const double q = std::frexp(double_multiplier, shift);
+ auto q_fixed = static_cast<int64_t>(round(q * (1ll << 31)));
+
+ assert(q_fixed <= (1ll << 31));
+ if (q_fixed == (1ll << 31))
+ {
+ q_fixed /= 2;
+ ++*shift;
+ }
+ assert(q_fixed <= std::numeric_limits<int32_t>::max());
+ // A shift amount smaller than -31 would cause all bits to be shifted out
+ // and thus all results would be zero. We implement that instead with
+ // q_fixed==0, so as to avoid hitting issues with right-shift
+ // operations with shift amounts greater than 31. Note that this happens
+ // roughly when abs(double_multiplier) < 2^-31 and the present handling means
+ // that we're effectively flushing tiny double_multiplier's to zero.
+ // We could conceivably handle values in the range (roughly) [32, 63]
+ // as 'denormals' i.e. (shift==0, q_fixed < 2^30). In that point of view
+ // the present handling is just doing 'flush denormals to zero'. We could
+ // reconsider and actually generate nonzero denormals if a need arises.
+ if (*shift < -31)
+ {
+ *shift = 0;
+ q_fixed = 0;
+ }
+ *quantized_multiplier = static_cast<int32_t>(q_fixed);
+}
+
+inline void QuantizeMultiplierSmallerThanOneExp(double double_multiplier,
+ int32_t *quantized_multiplier, int *left_shift)
+{
+ assert(double_multiplier < 1.0);
+ assert(double_multiplier > 0.0);
+ int shift;
+ QuantizeMultiplier(double_multiplier, quantized_multiplier, &shift);
+ assert(shift <= 0);
+ *left_shift = shift;
+}
+
+inline int32_t MaskIfNonZero(int32_t a)
+{
+ static const int32_t zero = 0;
+ return a ? ~zero : zero;
+}
+
+inline int32_t MaskIfZero(int32_t a) { return MaskIfNonZero(!a); }
+
+inline int32_t MaskIfLessThan(int32_t a, int32_t b) { return MaskIfNonZero(a < b); }
+
+inline int32_t MaskIfGreaterThan(int32_t a, int32_t b) { return MaskIfNonZero(a > b); }
+
+inline int32_t RoundingDivideByPOT(int32_t x, int exponent)
+{
+ assert(exponent >= 0);
+ assert(exponent <= 31);
+ const int32_t mask = (1ll << exponent) - 1;
+ const int32_t remainder = x & mask;
+ const int32_t threshold = (mask >> 1) + (MaskIfLessThan(x, 0) & 1);
+ return (x >> exponent) + (MaskIfGreaterThan(remainder, threshold) & 1);
+}
+
+inline std::int32_t SaturatingRoundingDoublingHighMul(std::int32_t a, std::int32_t b)
+{
+ bool overflow = a == b && a == std::numeric_limits<std::int32_t>::min();
+ std::int64_t a_64(a);
+ std::int64_t b_64(b);
+ std::int64_t ab_64 = a_64 * b_64;
+ std::int32_t nudge = ab_64 >= 0 ? (1 << 30) : (1 - (1 << 30));
+ std::int32_t ab_x2_high32 = static_cast<std::int32_t>((ab_64 + nudge) / (1ll << 31));
+ return overflow ? std::numeric_limits<std::int32_t>::max() : ab_x2_high32;
+}
+
+inline int32_t MultiplyByQuantizedMultiplier(int32_t x, int32_t quantized_multiplier, int shift)
+{
+ int left_shift = shift > 0 ? shift : 0;
+ int right_shift = shift > 0 ? 0 : -shift;
+ return RoundingDivideByPOT(
+ SaturatingRoundingDoublingHighMul(x * (1 << left_shift), quantized_multiplier), right_shift);
+}
+
+inline int32_t MultiplyByQuantizedMultiplierSmallerThanOneExp(int32_t x,
+ int32_t quantized_multiplier,
+ int left_shift)
+{
+ return RoundingDivideByPOT(SaturatingRoundingDoublingHighMul(x, quantized_multiplier),
+ -left_shift);
+}
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_QUANTIZATION_HELPERS_
diff --git a/compiler/mir-interpreter/src/ops/ReLU.cpp b/compiler/mir-interpreter/src/ops/ReLU.cpp
new file mode 100644
index 000000000..92d3ded5e
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/ReLU.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2019 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 "ReLU.h"
+#include "Common.h"
+
+#include <mir/ShapeRange.h>
+#include <mir/Tensor.h>
+
+#include <algorithm>
+
+namespace mir_interpreter
+{
+
+template <typename T> struct ReLUImpl
+{
+ static void run(const mir::TensorVariant &arg, mir::TensorVariant &result);
+};
+
+template <typename T>
+void ReLUImpl<T>::run(const mir::TensorVariant &arg, mir::TensorVariant &result)
+{
+ mir::Tensor<T> arg_accessor(arg);
+ mir::Tensor<T> res_accessor(result);
+
+ for (const auto &index : mir::ShapeRange(result.getShape()))
+ {
+ res_accessor.at(index) = std::max(arg_accessor.at(index), static_cast<T>(0));
+ }
+}
+
+template <> struct ReLUImpl<uint8_t>
+{
+ static void run(const mir::TensorVariant &arg, mir::TensorVariant &result)
+ {
+ throw std::runtime_error{"NYI"};
+ }
+};
+
+void ReLU(const mir::TensorVariant &arg, mir::TensorVariant &result)
+{
+ dispatch<ReLUImpl>(arg.getElementType(), arg, result);
+};
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/ReLU.h b/compiler/mir-interpreter/src/ops/ReLU.h
new file mode 100644
index 000000000..9edabb9d9
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/ReLU.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 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 _NNC_CORE_BACKEND_INTERPRETER_RELU_
+#define _NNC_CORE_BACKEND_INTERPRETER_RELU_
+
+#include <mir/TensorVariant.h>
+
+namespace mir_interpreter
+{
+
+void ReLU(const mir::TensorVariant &arg, mir::TensorVariant &result);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_RELU_
diff --git a/compiler/mir-interpreter/src/ops/ReduceMean.cpp b/compiler/mir-interpreter/src/ops/ReduceMean.cpp
new file mode 100644
index 000000000..ebaa3b48f
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/ReduceMean.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2020 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 _NNC_CORE_BACKEND_INTERPRETER_REDUCE_MEAN_
+#define _NNC_CORE_BACKEND_INTERPRETER_REDUCE_MEAN_
+
+#include "ReduceMean.h"
+#include "Common.h"
+
+#include "mir/ops/ReduceMeanOp.h"
+#include "mir/Tensor.h"
+#include "mir/ShapeRange.h"
+
+namespace mir_interpreter
+{
+
+template <typename T> struct ReduceMeanImpl
+{
+ static void run(const mir::TensorVariant &inputv, const mir::ops::ReduceMeanOp &op,
+ mir::TensorVariant &output);
+};
+
+template <typename T>
+void ReduceMeanImpl<T>::run(const mir::TensorVariant &inputv, const mir::ops::ReduceMeanOp &op,
+ mir::TensorVariant &output)
+{
+ const auto &input_shape = op.getInputShape(0);
+ const auto &output_shape = op.getOutputShape(0);
+ const auto &reduction_dims = op.getReductionDims();
+ const bool keep_dims = op.getKeepDims();
+
+ const auto reductor = [](T result, T x) { return result + x; };
+
+ mir::Tensor<T> input(inputv);
+ mir::Tensor<T> res_accessor(output);
+
+ erase<T>(output);
+
+ // This mask contains 'true' for dimensions that should be reduced. For example, if we want
+ // to reduce dimensions 1 and 3 with total number of dimensions of 4, the mask will be
+ // [false, true, false, true].
+ std::vector<bool> reduction_dims_mask(input_shape.rank(), false);
+ for (const int dim : reduction_dims)
+ {
+ reduction_dims_mask[dim] = true;
+ }
+
+ mir::Index out_index(output_shape.rank());
+ for (const mir::Index &in_index : mir::ShapeRange(input_shape))
+ {
+ int out_index_dim = 0;
+ for (int dim = 0; dim < input_shape.rank(); ++dim)
+ {
+ if (keep_dims)
+ {
+ out_index.at(out_index_dim++) = reduction_dims_mask[dim] ? 0 : in_index.at(dim);
+ }
+ else
+ {
+ if (!reduction_dims_mask[dim])
+ {
+ out_index.at(out_index_dim++) = in_index.at(dim);
+ }
+ }
+ }
+ res_accessor.at(out_index) = reductor(res_accessor.at(out_index), input.at(in_index));
+ }
+
+ const std::int32_t reduction_factor = input_shape.numElements() / output_shape.numElements();
+
+ for (const auto &index : mir::ShapeRange(output_shape))
+ {
+ res_accessor.at(index) /= reduction_factor;
+ }
+}
+
+void ReduceMean(const mir::TensorVariant &input, const mir::ops::ReduceMeanOp &op,
+ mir::TensorVariant &output)
+{
+ dispatch<ReduceMeanImpl>(input.getElementType(), input, op, output);
+};
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_REDUCE_MEAN_
diff --git a/compiler/mir-interpreter/src/ops/ReduceMean.h b/compiler/mir-interpreter/src/ops/ReduceMean.h
new file mode 100644
index 000000000..178563b2c
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/ReduceMean.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2019 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 _NNC_CORE_BACKEND_INTERPRETER_REDUCE_MEAN_
+#define _NNC_CORE_BACKEND_INTERPRETER_REDUCE_MEAN_
+
+#include "mir/ops/ReduceMeanOp.h"
+
+namespace mir_interpreter
+{
+
+void ReduceMean(const mir::TensorVariant &input, const mir::ops::ReduceMeanOp &op,
+ mir::TensorVariant &output);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_REDUCE_MEAN_
diff --git a/compiler/mir-interpreter/src/ops/Reshape.cpp b/compiler/mir-interpreter/src/ops/Reshape.cpp
new file mode 100644
index 000000000..f29b261ce
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Reshape.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2020 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 "Reshape.h"
+
+#include "mir/ShapeRange.h"
+
+#include <cstring>
+
+namespace mir_interpreter
+{
+
+void Reshape(const mir::TensorVariant &input, mir::TensorVariant &output)
+{
+ assert(input.getShape().numElements() == output.getShape().numElements());
+
+ mir::ShapeRange input_range(input.getShape());
+ auto in_iter = input_range.begin();
+ const size_t elem_size = input.getElementSize();
+
+ for (const auto &out_index : mir::ShapeRange(output.getShape()))
+ std::memcpy(output.at(out_index), input.at(*in_iter++), elem_size);
+}
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/Reshape.h b/compiler/mir-interpreter/src/ops/Reshape.h
new file mode 100644
index 000000000..2da6411f6
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Reshape.h
@@ -0,0 +1,29 @@
+/*
+ * 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 _NNC_CORE_BACKEND_INTERPRETER_RESHAPE_IMPL_
+#define _NNC_CORE_BACKEND_INTERPRETER_RESHAPE_IMPL_
+
+#include "mir/TensorVariant.h"
+
+namespace mir_interpreter
+{
+
+void Reshape(const mir::TensorVariant &input, mir::TensorVariant &output);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_RESHAPE_IMPL_
diff --git a/compiler/mir-interpreter/src/ops/Sigmoid.cpp b/compiler/mir-interpreter/src/ops/Sigmoid.cpp
new file mode 100644
index 000000000..23718f935
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Sigmoid.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2019 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 "Sigmoid.h"
+#include "Common.h"
+
+#include <mir/ShapeRange.h>
+#include <mir/Tensor.h>
+
+#include <cmath>
+
+namespace mir_interpreter
+{
+
+template <typename T> struct SigmoidImpl
+{
+ static void run(const mir::TensorVariant &arg, mir::TensorVariant &result);
+};
+
+template <typename T>
+void SigmoidImpl<T>::run(const mir::TensorVariant &arg, mir::TensorVariant &result)
+{
+ mir::Tensor<T> arg_accessor(arg);
+ mir::Tensor<T> res_accessor(result);
+
+ for (const auto &index : mir::ShapeRange(result.getShape()))
+ {
+ res_accessor.at(index) = 1.0f / (1.0f + std::exp(-arg_accessor.at(index)));
+ }
+}
+
+template <> struct SigmoidImpl<uint8_t>
+{
+ static void run(const mir::TensorVariant &arg, mir::TensorVariant &result)
+ {
+ throw std::runtime_error{"NYI"};
+ }
+};
+
+void Sigmoid(const mir::TensorVariant &arg, mir::TensorVariant &result)
+{
+ dispatch<SigmoidImpl>(arg.getElementType(), arg, result);
+};
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/Sigmoid.h b/compiler/mir-interpreter/src/ops/Sigmoid.h
new file mode 100644
index 000000000..81c614c89
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Sigmoid.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 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 _NNC_CORE_BACKEND_INTERPRETER_SIGMOID_
+#define _NNC_CORE_BACKEND_INTERPRETER_SIGMOID_
+
+#include <mir/TensorVariant.h>
+
+namespace mir_interpreter
+{
+
+void Sigmoid(const mir::TensorVariant &arg, mir::TensorVariant &result);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_SIGMOID_
diff --git a/compiler/mir-interpreter/src/ops/Slice.cpp b/compiler/mir-interpreter/src/ops/Slice.cpp
new file mode 100644
index 000000000..df24d49cd
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Slice.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020 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 "Slice.h"
+
+#include "Fill.h"
+#include "Common.h"
+
+#include "mir/Tensor.h"
+#include "mir/ShapeRange.h"
+
+namespace mir_interpreter
+{
+
+template <typename T> struct SliceImpl
+{
+ static void run(const mir::TensorVariant &arg, const mir::Shape &starts, mir::TensorVariant &res);
+};
+
+template <typename T>
+void SliceImpl<T>::run(const mir::TensorVariant &arg, const mir::Shape &starts,
+ mir::TensorVariant &res)
+{
+ mir::Tensor<T> input(arg);
+ mir::Tensor<T> output(res);
+
+ for (auto id : mir::ShapeRange(res.getShape()))
+ {
+ mir::Index idx = mir_interpreter::shift(id, starts);
+ output.at(id) = input.at(idx);
+ }
+}
+
+void Slice(const mir::TensorVariant &arg, const mir::Shape &starts, mir::TensorVariant &res)
+{
+ dispatch<SliceImpl>(arg.getElementType(), arg, starts, res);
+}
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/Slice.h b/compiler/mir-interpreter/src/ops/Slice.h
new file mode 100644
index 000000000..9e5e3bb0e
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Slice.h
@@ -0,0 +1,29 @@
+/*
+ * 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 _NNC_CORE_BACKEND_INTERPRETER_SLICE_
+#define _NNC_CORE_BACKEND_INTERPRETER_SLICE_
+
+#include "mir/TensorVariant.h"
+
+namespace mir_interpreter
+{
+
+void Slice(const mir::TensorVariant &arg, const mir::Shape &starts, mir::TensorVariant &res);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_DIV_
diff --git a/compiler/mir-interpreter/src/ops/Softmax.cpp b/compiler/mir-interpreter/src/ops/Softmax.cpp
new file mode 100644
index 000000000..f263f967d
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Softmax.cpp
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2019 The TensorFlow Authors. 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 "Softmax.h"
+#include "Common.h"
+#include "QuantizationHelpers.h"
+
+#include <mir/ShapeRange.h>
+#include <mir/Tensor.h>
+
+#include <cmath>
+
+namespace mir_interpreter
+{
+
+static inline void PopulateSoftmaxLookupTable(float *table, float input_scale, float beta)
+{
+ const float scale = -input_scale * beta;
+ const int32_t max_uint8 = std::numeric_limits<uint8_t>::max();
+ for (int32_t val = 0; val <= max_uint8; ++val)
+ table[max_uint8 - val] = expf(scale * val);
+}
+
+template <typename T> struct SoftmaxImpl
+{
+ static void run(const mir::TensorVariant &arg, int axis, mir::TensorVariant &result);
+};
+
+template <typename T>
+void SoftmaxImpl<T>::run(const mir::TensorVariant &arg, int axis, mir::TensorVariant &result)
+{
+ mir::Tensor<T> arg_accessor(arg);
+ mir::Tensor<T> res_accessor(result);
+
+ mir::Shape expsum_shape = arg.getShape();
+ expsum_shape.dim(axis) = 1;
+ mir::TensorType expsum_type(arg.getElementType(), expsum_shape);
+ mir::TensorVariant expsum(expsum_type);
+ mir::Tensor<T> expsum_accessor(expsum);
+
+ for (const auto &expsum_index : mir::ShapeRange(expsum_shape))
+ {
+ T sum = 0;
+ mir::Index arg_index = expsum_index;
+ std::int32_t axis_size = arg.getShape().dim(axis);
+ for (std::int32_t i = 0; i < axis_size; ++i)
+ {
+ arg_index.at(axis) = i;
+ sum += std::exp(arg_accessor.at(arg_index));
+ }
+ expsum_accessor.at(expsum_index) = sum;
+ }
+
+ for (const auto &res_index : mir::ShapeRange(result.getShape()))
+ {
+ mir::Index expsum_index = res_index;
+ expsum_index.at(axis) = 0;
+ res_accessor.at(res_index) =
+ std::exp(arg_accessor.at(res_index)) / expsum_accessor.at(expsum_index);
+ }
+}
+
+template <> struct SoftmaxImpl<uint8_t>
+{
+ static void run(const mir::TensorVariant &input, int axis, mir::TensorVariant &output);
+};
+
+void SoftmaxImpl<uint8_t>::run(const mir::TensorVariant &input, int axis,
+ mir::TensorVariant &output)
+{
+ const auto &input_type = input.getType();
+ const auto &output_type = output.getType();
+
+ assert(input_type.isQuantized());
+ assert(output_type.isQuantized());
+
+ const auto input_shape = input_type.getShape();
+
+ assert(input_type.getElementType() == mir::DataType::UINT8);
+ assert(axis == input_shape.rank() - 1); // supported only last dim axis
+ (void)axis;
+
+ double input_scale = input_type.getQuantization().getScale();
+ double output_scale = output_type.getQuantization().getScale();
+
+ const int trailing_dim = input_shape.rank() - 1;
+ int excluding_last_dim = 1;
+ for (int32_t i = 0; i < input_shape.rank() - 1; i++)
+ {
+ excluding_last_dim *= input_shape.dim(i);
+ }
+ const int last_dim = input_shape.dim(trailing_dim);
+
+ const int32_t clamp_max = std::numeric_limits<uint8_t>::max();
+ const int32_t clamp_min = std::numeric_limits<uint8_t>::min();
+
+ uint8_t *input_data = reinterpret_cast<uint8_t *>(input.atOffset(0));
+
+ float table[256];
+ PopulateSoftmaxLookupTable(table, input_scale, 1.f);
+
+ uint8_t *output_data = reinterpret_cast<uint8_t *>(output.atOffset(0));
+
+ for (int i = 0; i < excluding_last_dim; ++i)
+ {
+ int32_t max_val = std::numeric_limits<uint8_t>::min();
+ // Find max quantized value.
+ for (int j = 0; j < last_dim; ++j)
+ {
+ max_val = std::max(max_val, static_cast<int32_t>(input_data[j]));
+ }
+
+ float sum_exp = 0.0f;
+ const int32_t max_uint8 = std::numeric_limits<uint8_t>::max();
+ const float *table_offset = &table[max_uint8 - max_val];
+ // Calculate normalizer sum(exp(x)).
+ for (int j = 0; j < last_dim; ++j)
+ {
+ sum_exp += table_offset[input_data[j]];
+ }
+
+ const float inv_sum_exp = 1.0f / (sum_exp * output_scale);
+ // Normalize and quantize probabilities.
+ for (int j = 0; j < last_dim; ++j)
+ {
+ const float prob_rescaled = table_offset[input_data[j]] * inv_sum_exp;
+ const int32_t prob_quantized = static_cast<int32_t>(prob_rescaled + 0.5);
+ output_data[j] =
+ static_cast<uint8_t>(std::max(std::min(clamp_max, prob_quantized), clamp_min));
+ }
+ input_data += last_dim;
+ output_data += last_dim;
+ }
+}
+
+void Softmax(const mir::TensorVariant &arg, int axis, mir::TensorVariant &result)
+{
+ dispatch<SoftmaxImpl>(arg.getElementType(), arg, axis, result);
+};
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/Softmax.h b/compiler/mir-interpreter/src/ops/Softmax.h
new file mode 100644
index 000000000..9c9818c70
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Softmax.h
@@ -0,0 +1,29 @@
+/*
+ * 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 _NNC_CORE_BACKEND_INTERPRETER_SOFTMAX_
+#define _NNC_CORE_BACKEND_INTERPRETER_SOFTMAX_
+
+#include <mir/TensorVariant.h>
+
+namespace mir_interpreter
+{
+
+void Softmax(const mir::TensorVariant &arg, int axis, mir::TensorVariant &result);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_SOFTMAX_
diff --git a/compiler/mir-interpreter/src/ops/Sqrt.cpp b/compiler/mir-interpreter/src/ops/Sqrt.cpp
new file mode 100644
index 000000000..7a2ca49c8
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Sqrt.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2019 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 "Sqrt.h"
+#include "Common.h"
+
+#include <mir/ShapeRange.h>
+#include <mir/Tensor.h>
+
+#include <cmath>
+
+namespace mir_interpreter
+{
+
+template <typename T> struct SqrtImpl
+{
+ static void run(const mir::TensorVariant &arg, mir::TensorVariant &result);
+};
+
+template <typename T>
+void SqrtImpl<T>::run(const mir::TensorVariant &arg, mir::TensorVariant &result)
+{
+ mir::Tensor<T> arg_accessor(arg);
+ mir::Tensor<T> res_accessor(result);
+
+ for (const auto &index : mir::ShapeRange(result.getShape()))
+ {
+ res_accessor.at(index) = std::sqrt(arg_accessor.at(index));
+ }
+}
+
+template <> struct SqrtImpl<uint8_t>
+{
+ static void run(const mir::TensorVariant &arg, mir::TensorVariant &result)
+ {
+ throw std::runtime_error{"NYI"};
+ }
+};
+
+void Sqrt(const mir::TensorVariant &arg, mir::TensorVariant &result)
+{
+ dispatch<SqrtImpl>(arg.getElementType(), arg, result);
+};
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/Sqrt.h b/compiler/mir-interpreter/src/ops/Sqrt.h
new file mode 100644
index 000000000..fef2bf0fe
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Sqrt.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 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 _NNC_CORE_BACKEND_INTERPRETER_SQRT_
+#define _NNC_CORE_BACKEND_INTERPRETER_SQRT_
+
+#include <mir/TensorVariant.h>
+
+namespace mir_interpreter
+{
+
+void Sqrt(const mir::TensorVariant &arg, mir::TensorVariant &result);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_SQRT_
diff --git a/compiler/mir-interpreter/src/ops/Sub.cpp b/compiler/mir-interpreter/src/ops/Sub.cpp
new file mode 100644
index 000000000..6c03cff82
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Sub.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2020 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 "Sub.h"
+#include "Common.h"
+
+#include "mir/ShapeRange.h"
+#include "mir/Tensor.h"
+
+namespace mir_interpreter
+{
+
+using namespace mir;
+
+template <typename T> struct SubImpl
+{
+ static void run(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res);
+};
+
+template <typename T>
+void SubImpl<T>::run(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res)
+{
+ TensorVariant broadcasted_lhs(lhs, res.getShape());
+ TensorVariant broadcasted_rhs(rhs, res.getShape());
+ Tensor<T> lhs_accessor(broadcasted_lhs);
+ Tensor<T> rhs_accessor(broadcasted_rhs);
+ Tensor<T> res_accessor(res);
+
+ for (const auto &index : ShapeRange(res.getShape()))
+ {
+ res_accessor.at(index) = lhs_accessor.at(index) - rhs_accessor.at(index);
+ }
+}
+
+template <> struct SubImpl<uint8_t>
+{
+ static void run(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res)
+ {
+ throw std::runtime_error{"NYI"};
+ }
+};
+
+void Sub(const TensorVariant &lhs, const TensorVariant &rhs, TensorVariant &res)
+{
+ dispatch<SubImpl>(lhs.getElementType(), lhs, rhs, res);
+};
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/Sub.h b/compiler/mir-interpreter/src/ops/Sub.h
new file mode 100644
index 000000000..53991596f
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Sub.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 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 _NNC_CORE_BACKEND_INTERPRETER_SUB_
+#define _NNC_CORE_BACKEND_INTERPRETER_SUB_
+
+#include "mir/TensorVariant.h"
+
+namespace mir_interpreter
+{
+
+void Sub(const mir::TensorVariant &lhs, const mir::TensorVariant &rhs, mir::TensorVariant &res);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_SUB_
diff --git a/compiler/mir-interpreter/src/ops/Tanh.cpp b/compiler/mir-interpreter/src/ops/Tanh.cpp
new file mode 100644
index 000000000..49a3461bf
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Tanh.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2019 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 "Sigmoid.h"
+#include "Common.h"
+
+#include <mir/ShapeRange.h>
+#include <mir/Tensor.h>
+
+#include <cmath>
+
+namespace mir_interpreter
+{
+
+template <typename T> struct TanhImpl
+{
+ static void run(const mir::TensorVariant &arg, mir::TensorVariant &result);
+};
+
+template <typename T>
+void TanhImpl<T>::run(const mir::TensorVariant &arg, mir::TensorVariant &result)
+{
+ mir::Tensor<T> arg_accessor(arg);
+ mir::Tensor<T> res_accessor(result);
+
+ for (const auto &index : mir::ShapeRange(result.getShape()))
+ {
+ res_accessor.at(index) = std::tanh(arg_accessor.at(index));
+ }
+}
+
+template <> struct TanhImpl<uint8_t>
+{
+ static void run(const mir::TensorVariant &arg, mir::TensorVariant &result)
+ {
+ throw std::runtime_error{"NYI"};
+ }
+};
+
+void Tanh(const mir::TensorVariant &arg, mir::TensorVariant &result)
+{
+ dispatch<TanhImpl>(arg.getElementType(), arg, result);
+};
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/Tanh.h b/compiler/mir-interpreter/src/ops/Tanh.h
new file mode 100644
index 000000000..2f376f5bd
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Tanh.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 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 _NNC_CORE_BACKEND_INTERPRETER_TANH_
+#define _NNC_CORE_BACKEND_INTERPRETER_TANH_
+
+#include <mir/TensorVariant.h>
+
+namespace mir_interpreter
+{
+
+void Tanh(const mir::TensorVariant &arg, mir::TensorVariant &result);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_TANH_
diff --git a/compiler/mir-interpreter/src/ops/Transpose.cpp b/compiler/mir-interpreter/src/ops/Transpose.cpp
new file mode 100644
index 000000000..1f0ad56c3
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Transpose.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2020 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 "Transpose.h"
+
+#include "mir/ShapeRange.h"
+#include "mir/Tensor.h"
+
+#include "Common.h"
+
+namespace mir_interpreter
+{
+
+template <typename T> struct TransposeImpl
+{
+ static void run(const mir::TensorVariant &input, const mir::ops::TransposeOp &op,
+ mir::TensorVariant &output);
+};
+
+template <typename T>
+void TransposeImpl<T>::run(const mir::TensorVariant &inputv, const mir::ops::TransposeOp &op,
+ mir::TensorVariant &outputv)
+{
+ const auto &output_shape = op.getOutputShape(0);
+ const auto &axis_order = op.getAxisOrder();
+ const int32_t num_axis = static_cast<int32_t>(axis_order.size());
+ assert(num_axis == inputv.getShape().rank());
+ assert(num_axis == output_shape.rank());
+
+ mir::Index output_index;
+ output_index.resize(num_axis);
+
+ mir::Tensor<T> input(inputv);
+ mir::Tensor<T> output(outputv);
+
+ for (auto &input_index : mir::ShapeRange(input.getShape()))
+ {
+ for (int32_t i = 0; i < num_axis; i++)
+ output_index.at(i) = input_index.at(axis_order[i]);
+
+ output.at(output_index) = input.at(input_index);
+ }
+}
+
+void Transpose(const mir::TensorVariant &input, const mir::ops::TransposeOp &op,
+ mir::TensorVariant &output)
+{
+ dispatch<TransposeImpl>(input.getElementType(), input, op, output);
+}
+
+} // namespace mir_interpreter
diff --git a/compiler/mir-interpreter/src/ops/Transpose.h b/compiler/mir-interpreter/src/ops/Transpose.h
new file mode 100644
index 000000000..f60ed7295
--- /dev/null
+++ b/compiler/mir-interpreter/src/ops/Transpose.h
@@ -0,0 +1,31 @@
+/*
+ * 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 _NNC_CORE_BACKEND_INTERPRETER_TRANSPOSE_
+#define _NNC_CORE_BACKEND_INTERPRETER_TRANSPOSE_
+
+#include "mir/TensorVariant.h"
+#include "mir/ops/TransposeOp.h"
+
+namespace mir_interpreter
+{
+
+void Transpose(const mir::TensorVariant &input, const mir::ops::TransposeOp &op,
+ mir::TensorVariant &output);
+
+} // namespace mir_interpreter
+
+#endif //_NNC_CORE_BACKEND_INTERPRETER_TRANSPOSE_