diff options
Diffstat (limited to 'compiler/mir-interpreter/src')
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_ |