#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"
{
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)
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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__ */