ArgMinMax Implementation for ArgMin and ArgMax. (#3331)
authorPRAVEEN DORESWAMY NAIDU/System SW /SRI-Bangalore/Staff Engineer/삼성전자 <praveen.dn@samsung.com>
Wed, 7 Nov 2018 08:17:28 +0000 (13:47 +0530)
committer박세희/동작제어Lab(SR)/Principal Engineer/삼성전자 <saehie.park@samsung.com>
Wed, 7 Nov 2018 08:17:28 +0000 (17:17 +0900)
This change introduces the cpu kernel for the ArgMin and ArgMax
operations.

Signed-off-by: Praveen D N <praveen.dn@samsung.com>
runtimes/pure_arm_compute/src/compilation.cc
runtimes/pure_arm_compute/src/internal/layers/ArgMinMax.cc [new file with mode: 0644]
runtimes/pure_arm_compute/src/internal/layers/ArgMinMax.h [new file with mode: 0644]

index 493208c..deeb786 100644 (file)
@@ -99,6 +99,7 @@
 #include "internal/layers/HashtableLookupLayer.h"
 #include "internal/layers/SimpleSpaceToBatchND.h"
 #include "internal/layers/SimpleNeg.h"
+#include "internal/layers/ArgMinMax.h"
 
 #include "util/matrix/IndexIterator.h"
 #include "util/kernel/IndexIterator.h"
@@ -3516,7 +3517,71 @@ void Planner::visit(const ::internal::tflite::op::ArgMax::Node &node)
 {
   VERBOSE(ArgMax) << "Configure ARGMAX operation" << std::endl;
 
-  throw std::runtime_error("Not supported, yet");
+  const ::internal::tflite::operand::Index ofm_index{node.param().ofm_index};
+  const ::internal::tflite::operand::Index ifm_index{node.param().ifm_index};
+  const ::internal::tflite::operand::Index axis_index{node.param().axis_index};
+
+  auto ifm_shape = _ctx.at(ifm_index).shape();
+  auto ofm_shape = _ctx.at(ofm_index).shape();
+  auto axis_shape = _ctx.at(axis_index).shape();
+
+  assert(_ctx.at(axis_index).hasData());
+  // Axis dimension is always 1.
+  assert(axis_shape.rank() == 1);
+  assert(ifm_shape.rank() == ofm_shape.rank());
+
+  _builder.addShapeConstr(ofm_index, asTensorInfo(asTensorShape(_ctx.at(ofm_index).shape()),
+                                                  _ctx.at(ofm_index).type()));
+  _builder.addShapeConstr(ifm_index, asTensorInfo(asTensorShape(_ctx.at(ifm_index).shape()),
+                                                  _ctx.at(ifm_index).type()));
+
+  std::vector<uint32_t> l_axis;
+  const auto axis_size = _ctx.at(axis_index).shape().asVector();
+  auto axis_base = _ctx.at(axis_index).data().base();
+  auto axis_type = _ctx.at(axis_index).type();
+  // TODO Should support axis size > 1.
+  assert(axis_size == 1);
+  // axis is tensor with 1 dimension - always a vector.
+  assert(axis_base != nullptr);
+  for (uint32_t n = 0; n < axis_size; ++n)
+  {
+    int32_t axis_value = *(reinterpret_cast<const int32_t *>(axis_base) + n);
+    if (axis_value < 0)
+    {
+      axis_value += ifm_shape.rank();
+    }
+    l_axis.push_back(ToARMComputeAxis(ifm_shape.rank(), axis_value).value());
+  }
+
+  // Construct operation parameters
+  struct Param
+  {
+    int ofm_index;
+    int ifm_index;
+    std::vector<uint32_t> axis;
+    int rank;
+  };
+
+  Param param;
+
+  param.ofm_index = ofm_index.asInt();
+  param.ifm_index = ifm_index.asInt();
+  param.axis = l_axis;
+  param.rank = ifm_shape.rank();
+
+  auto stage = [param](const IAllocationContext &ctx, IExecutionBuilder &builder) {
+    auto ofm_alloc = ctx.at(::internal::tflite::operand::Index{param.ofm_index});
+    auto ifm_alloc = ctx.at(::internal::tflite::operand::Index{param.ifm_index});
+
+    auto fn = nnfw::make_unique<ArgMinMax>();
+    bool is_min = false, is_max = true;
+
+    fn->configure(CAST_CL(ifm_alloc), CAST_CL(ofm_alloc), param.axis, param.rank, is_min, is_max);
+
+    builder.append("ArgMax", std::move(fn));
+  };
+
+  _builder.addStage(stage);
 }
 
 void Planner::visit(const ::internal::tflite::op::SQRT::Node &node)
diff --git a/runtimes/pure_arm_compute/src/internal/layers/ArgMinMax.cc b/runtimes/pure_arm_compute/src/internal/layers/ArgMinMax.cc
new file mode 100644 (file)
index 0000000..4460d4f
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * 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 "internal/layers/ArgMinMax.h"
+#include <arm_compute/runtime/CL/CLScheduler.h>
+#include <arm_compute/core/CL/CLHelpers.h>
+
+void ArgMinMax::configure(::arm_compute::ITensor *input, ::arm_compute::ITensor *output,
+                          std::vector<uint32_t> axis, int rank, bool is_min, bool is_max)
+{
+  _input = input;
+  _output = output;
+  _axis = axis;
+  _input_rank = rank;
+  _is_min = is_min;
+  _is_max = is_max;
+}
+
+inline const ::arm_compute::TensorShape
+inferOutputShape(const ::arm_compute::TensorShape &input_shape, const std::vector<uint32_t> &axis,
+                 int input_rank)
+{
+  ::arm_compute::TensorShape out_shape{};
+  size_t dim = 1;
+  for (int i = 0; i < input_rank; ++i)
+  {
+    dim = input_shape[i];
+    out_shape.set(i, dim);
+  }
+
+  for (int i = 0; i < axis.size(); ++i)
+  {
+    out_shape.set(axis[i], 1);
+  }
+
+  return out_shape;
+}
+
+template <typename T>
+inline T
+getArgMinMaxEle(const ::arm_compute::ITensor *input, const ::arm_compute::TensorShape &input_shape,
+                const ::arm_compute::TensorShape &output_shape, const size_t b, const size_t d,
+                const size_t h, const size_t w, const int axis, bool is_min, bool is_max)
+{
+  // If output[dimention] == 1, will check all values of that dimension because of reducing
+  // dimension.
+  // Else will check only one value.
+  const size_t start_b = output_shape[3] == 1 ? 0 : b;
+  const size_t start_d = output_shape[2] == 1 ? 0 : d;
+  const size_t start_h = output_shape[1] == 1 ? 0 : h;
+  const size_t start_w = output_shape[0] == 1 ? 0 : w;
+  const size_t stop_b = output_shape[3] == 1 ? input_shape[3] - 1 : b;
+  const size_t stop_d = output_shape[2] == 1 ? input_shape[2] - 1 : d;
+  const size_t stop_h = output_shape[1] == 1 ? input_shape[1] - 1 : h;
+  const size_t stop_w = output_shape[0] == 1 ? input_shape[0] - 1 : w;
+
+  ::arm_compute::Coordinates id{w, h, d, b};
+  ::arm_compute::Coordinates min_max_id{w, h, d, b};
+
+  T value = *reinterpret_cast<T *>(input->ptr_to_element(id));
+  T tval = *reinterpret_cast<T *>(input->ptr_to_element(id));
+
+  for (size_t in_b = start_b; in_b <= stop_b; ++in_b)
+  {
+    id.set(3, in_b);
+    for (size_t in_d = start_d; in_d <= stop_d; ++in_d)
+    {
+      id.set(2, in_d);
+      for (size_t in_h = start_h; in_h <= stop_h; ++in_h)
+      {
+        id.set(1, in_h);
+        for (size_t in_w = start_w; in_w <= stop_w; ++in_w)
+        {
+          id.set(0, in_w);
+          if (is_min)
+          {
+            value = std::min<T>(value, *reinterpret_cast<T *>(input->ptr_to_element(id)));
+          }
+          else if (is_max)
+          {
+            value = std::max<T>(value, *reinterpret_cast<T *>(input->ptr_to_element(id)));
+          }
+          if (tval != value)
+          {
+            min_max_id = id;
+            tval = value;
+          }
+        }
+      }
+    }
+  }
+
+  return min_max_id[axis];
+}
+
+template <typename T>
+inline void getArgMinMax(const ::arm_compute::ITensor *input,
+                         const ::arm_compute::TensorShape &input_shape,
+                         const ::arm_compute::TensorShape &output_shape,
+                         ::arm_compute::ITensor *output, const int axis, bool is_min, bool is_max)
+{
+  ::arm_compute::Coordinates id;
+  for (size_t out_b = 0; out_b < output_shape[3]; ++out_b)
+  {
+    id.set(3, out_b);
+    for (size_t out_d = 0; out_d < output_shape[2]; ++out_d)
+    {
+      id.set(2, out_d);
+      for (size_t out_h = 0; out_h < output_shape[1]; ++out_h)
+      {
+        id.set(1, out_h);
+        for (size_t out_w = 0; out_w < output_shape[0]; ++out_w)
+        {
+          id.set(0, out_w);
+          *reinterpret_cast<int *>(output->ptr_to_element(id)) = getArgMinMaxEle<T>(
+              input, input_shape, output_shape, out_b, out_d, out_h, out_w, axis, is_min, is_max);
+        }
+      }
+    }
+  }
+}
+
+void ArgMinMax::run()
+{
+  if (::internal::arm_compute::isGpuMode())
+  {
+    auto &q = ::arm_compute::CLScheduler::get().queue();
+
+    CAST_CL(_input)->map(q);
+    CAST_CL(_output)->map(q);
+  }
+
+  ::arm_compute::TensorShape input_shape = _input->info()->tensor_shape();
+
+  // Axis dimension is 1 and size is 1.
+  // TODO support axis size > 1.
+  int axis_val = _axis[0];
+  ::arm_compute::TensorShape output_shape = inferOutputShape(input_shape, _axis, _input_rank);
+
+  _output->info()->set_tensor_shape(output_shape);
+  switch (_input->info()->data_type())
+  {
+    case ::arm_compute::DataType::QASYMM8:
+      getArgMinMax<uint8_t>(_input, input_shape, output_shape, _output, axis_val, _is_min, _is_max);
+      break;
+    case ::arm_compute::DataType::S32:
+      getArgMinMax<int32_t>(_input, input_shape, output_shape, _output, axis_val, _is_min, _is_max);
+      break;
+    case ::arm_compute::DataType::F32:
+      getArgMinMax<float>(_input, input_shape, output_shape, _output, axis_val, _is_min, _is_max);
+      break;
+    defualt:
+      ARM_COMPUTE_ERROR("DataType not supported");
+      break;
+  }
+
+  _output->info()->set_tensor_shape(output_shape);
+
+  if (::internal::arm_compute::isGpuMode())
+  {
+    auto &q = ::arm_compute::CLScheduler::get().queue();
+
+    CAST_CL(_input)->unmap(q);
+    CAST_CL(_output)->unmap(q);
+  }
+}
diff --git a/runtimes/pure_arm_compute/src/internal/layers/ArgMinMax.h b/runtimes/pure_arm_compute/src/internal/layers/ArgMinMax.h
new file mode 100644 (file)
index 0000000..88a454d
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * 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 __ARG_MIN_MAX_H__
+#define __ARG_MIN_MAX_H__
+
+#include "internal/arm_compute.h"
+#include "internal/arm_compute/Cast.h"
+#include <arm_compute/core/ITensor.h>
+#include <arm_compute/runtime/IFunction.h>
+
+class ArgMinMax : public ::arm_compute::IFunction
+{
+public:
+  ArgMinMax(void)
+      : _input(nullptr), _output(nullptr), _axis(), _input_rank(0), _is_min(false), _is_max(false)
+  {
+    // DO NOTHING
+  }
+
+public:
+  /** Initialise input and output
+   *
+   * @param[in]  input       First tensor input.
+   * @param[out] output      Output tensor.
+   * @param[in]  axis        Dimension along which to find Min or Max Index.
+   * @param[in]  input_rank  Rank of input tensor.
+   * @param[in]  is_min      True for ArgMin.
+   * @param[in]  is_max      True for ArgMax.
+   */
+
+  void configure(::arm_compute::ITensor *input, ::arm_compute::ITensor *output,
+                 std::vector<uint32_t> axis, int rank, bool is_min, bool is_max);
+
+  void run() override;
+
+private:
+  ::arm_compute::ITensor *_input;
+  ::arm_compute::ITensor *_output;
+  std::vector<uint32_t> _axis;
+  int _input_rank;
+  bool _is_min;
+  bool _is_max;
+};
+
+#endif /*__ARG_MIN_MAX_H__ */