ONE - On-device Neural Engine
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
Concatenation.h
Go to the documentation of this file.
1/*
2 * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
3 * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18#ifndef __NNFW_CKER_CONCATENATION_H__
19#define __NNFW_CKER_CONCATENATION_H__
20
21#include "cker/Shape.h"
22#include "cker/Types.h"
23
24#include <cstdint>
25#include <cmath>
26
27namespace nnfw
28{
29namespace cker
30{
31
32template <typename Scalar>
33inline void Concatenation(const ConcatenationParams &params, const Shape *const *input_shapes,
34 const Scalar *const *input_data, const Shape &output_shape,
35 Scalar *output_data)
36{
37 int axis = params.axis;
38 int inputs_count = params.inputs_count;
39 const int concat_dimensions = output_shape.DimensionsCount();
40 assert(axis < concat_dimensions);
41
42 [[maybe_unused]] int64_t concat_size = 0;
43 for (int i = 0; i < inputs_count; i++)
44 {
45 assert(input_shapes[i]->DimensionsCount() == concat_dimensions);
46 for (int j = 0; j < concat_dimensions; j++)
47 {
48 if (j != axis)
49 {
50 [[maybe_unused]] auto dim_checked = MatchingDim(*input_shapes[i], j, output_shape, j);
51 }
52 }
53 concat_size += input_shapes[i]->Dims(axis);
54 }
55 assert(concat_size == output_shape.Dims(axis));
56 int64_t outer_size = 1;
57 for (int i = 0; i < axis; ++i)
58 {
59 outer_size *= output_shape.Dims(i);
60 }
61 // For all input arrays,
62 // FlatSize() = outer_size * Dims(axis) * base_inner_size;
63 int64_t base_inner_size = 1;
64 for (int i = axis + 1; i < concat_dimensions; ++i)
65 {
66 base_inner_size *= output_shape.Dims(i);
67 }
68
69 Scalar *output_ptr = output_data;
70 for (int k = 0; k < outer_size; k++)
71 {
72 for (int i = 0; i < inputs_count; ++i)
73 {
74 const int copy_size = input_shapes[i]->Dims(axis) * base_inner_size;
75 memcpy(output_ptr, input_data[i] + k * copy_size, copy_size * sizeof(Scalar));
76 output_ptr += copy_size;
77 }
78 }
79}
80
81// quantized as it takes scale as a floating point value. This should be fixed
82// when optimizng this routine further.
84 const Shape *const *input_shapes,
85 const uint8_t *const *input_data, const Shape &output_shape,
86 uint8_t *output_data)
87{
88 int axis = params.axis;
89 const int32_t *input_zeropoint = params.input_zeropoint;
90 const float *input_scale = params.input_scale;
91 int inputs_count = params.inputs_count;
92 const int32_t output_zeropoint = params.output_zeropoint;
93 const float output_scale = params.output_scale;
94
95 const int concat_dimensions = output_shape.DimensionsCount();
96 assert(axis <= concat_dimensions);
97
98 [[maybe_unused]] int64_t concat_size = 0;
99 for (int i = 0; i < inputs_count; i++)
100 {
101 assert(input_shapes[i]->DimensionsCount() == concat_dimensions);
102 for (int j = 0; j < concat_dimensions; j++)
103 {
104 if (j != axis)
105 {
106 assert(input_shapes[i]->Dims(j) == output_shape.Dims(j));
107 }
108 }
109 concat_size += input_shapes[i]->Dims(axis);
110 }
111 assert(concat_size == output_shape.Dims(axis));
112 int64_t outer_size = 1;
113 for (int i = 0; i < axis; ++i)
114 {
115 outer_size *= output_shape.Dims(i);
116 }
117 // For all input arrays,
118 // FlatSize() = outer_size * Dims(axis) * base_inner_size;
119 int64_t base_inner_size = 1;
120 for (int i = axis + 1; i < concat_dimensions; ++i)
121 {
122 base_inner_size *= output_shape.Dims(i);
123 }
124
125 const float inverse_output_scale = 1.f / output_scale;
126 uint8_t *output_ptr = output_data;
127 for (int k = 0; k < outer_size; k++)
128 {
129 for (int i = 0; i < inputs_count; ++i)
130 {
131 const int copy_size = input_shapes[i]->Dims(axis) * base_inner_size;
132 const uint8_t *input_ptr = input_data[i] + k * copy_size;
133 if (input_zeropoint[i] == output_zeropoint && input_scale[i] == output_scale)
134 {
135 memcpy(output_ptr, input_ptr, copy_size);
136 }
137 else
138 {
139 const float scale = input_scale[i] * inverse_output_scale;
140 const float bias = -input_zeropoint[i] * scale;
141 for (int j = 0; j < copy_size; ++j)
142 {
143 const int32_t value =
144 static_cast<int32_t>(std::round(input_ptr[j] * scale + bias)) + output_zeropoint;
145 output_ptr[j] = static_cast<uint8_t>(std::max(std::min(255, value), 0));
146 }
147 }
148 output_ptr += copy_size;
149 }
150 }
151}
152
153} // namespace cker
154} // namespace nnfw
155
156#endif // __NNFW_CKER_CONCATENATION_H__
int32_t Dims(int i) const
Definition Shape.h:92
const luci_interpreter::RuntimeShape output_shape
void ConcatenationWithScaling(const ConcatenationParams &params, const Shape *const *input_shapes, const uint8_t *const *input_data, const Shape &output_shape, uint8_t *output_data)
int MatchingDim(const Shape &shape1, int index1, const Shape &shape2, int index2)
Definition Shape.h:220
void Concatenation(const ConcatenationParams &params, const Shape *const *input_shapes, const Scalar *const *input_data, const Shape &output_shape, Scalar *output_data)
Definition topk_v2.h:30
Definition Dims.h:26
const int32_t * input_zeropoint
Definition Types.h:224