1 /**
2 * Copyright 2020 Huawei Technologies Co., Ltd
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #include "src/runtime/kernel/arm/fp32/sparse_to_dense_fp32.h"
17 #include <vector>
18 #include <limits>
19 #include "include/errorcode.h"
20 #include "nnacl/fp32/sparse_to_dense_fp32.h"
21 #include "schema/model_generated.h"
22 #include "schema/ops_generated.h"
23 #include "src/kernel_registry.h"
24
25 using mindspore::kernel::KERNEL_ARCH;
26 using mindspore::lite::KernelRegistrar;
27 using mindspore::lite::RET_ERROR;
28 using mindspore::lite::RET_OK;
29 using mindspore::schema::PrimitiveType_SparseToDense;
30
31 namespace mindspore::kernel {
Init()32 int SparseToDenseCPUKernel::Init() {
33 MS_CHECK_TRUE_RET(in_tensors_.size() == kInputSize2, RET_ERROR);
34 CHECK_NULL_RETURN(in_tensors_[0]);
35 CHECK_NULL_RETURN(in_tensors_[1]);
36 CHECK_NULL_RETURN(in_tensors_[DIMENSION_2D]);
37 MS_CHECK_TRUE_RET(out_tensors_.size() == 1, RET_ERROR);
38 CHECK_NULL_RETURN(out_tensors_.front());
39 auto input2 = in_tensors_.at(2);
40 auto input3 = in_tensors_.at(3);
41 sparse_values = reinterpret_cast<float *>(input2->MutableData());
42 CHECK_NULL_RETURN(sparse_values);
43 CHECK_NULL_RETURN(input3->MutableData());
44 default_value = reinterpret_cast<float *>(input3->MutableData())[0];
45 if (input2->ElementsNum() == 1) {
46 isScalar = true;
47 }
48 if (!InferShapeDone()) {
49 return RET_OK;
50 }
51 return ReSize();
52 }
53
ReSize()54 int SparseToDenseCPUKernel::ReSize() {
55 auto output0 = out_tensors_.at(0);
56 std::vector<int> out_shape_tensor = output0->shape();
57 auto output_shape_tmp = reinterpret_cast<int *>(out_shape_tensor.data());
58 int output_dim = static_cast<int>(output0->shape().size());
59 MS_CHECK_TRUE_MSG(output_dim <= DIMENSION_4D, RET_ERROR, "output_dim should <= 4");
60 for (int i = 0; i < DIMENSION_4D - output_dim; i++) {
61 output_shape[i] = 1;
62 }
63 for (int i = 0; i < output_dim; i++) {
64 output_shape[i + DIMENSION_4D - output_dim] = output_shape_tmp[i];
65 }
66 output_num = output0->ElementsNum();
67 return RET_OK;
68 }
69
DoExcute(int task_id)70 int SparseToDenseCPUKernel::DoExcute(int task_id) {
71 int real_dst_count = MSMIN(index_num - task_id * count_unit_, count_unit_);
72 if (real_dst_count <= 0) {
73 return RET_OK;
74 }
75 int index_start = task_id * count_unit_;
76 int index_end = index_start + real_dst_count;
77 MS_CHECK_FALSE_MSG(index_num == 0, RET_ERROR, "div zero");
78 int out_width = output_num / index_num;
79 CHECK_NULL_RETURN(sparse_indices_vect);
80 CHECK_NULL_RETURN(sparse_values);
81 CHECK_NULL_RETURN(output_data);
82 SparseToDense(sparse_indices_vect, output_shape, sparse_values, default_value, output_data, isScalar, index_start,
83 index_end, out_width);
84 return RET_OK;
85 }
86
SparseToDenseRun(void * cdata,int task_id,float lhs_scale,float rhs_scale)87 int SparseToDenseRun(void *cdata, int task_id, float lhs_scale, float rhs_scale) {
88 auto s2ddata = reinterpret_cast<SparseToDenseCPUKernel *>(cdata);
89 auto ret = s2ddata->DoExcute(task_id);
90 if (ret != RET_OK) {
91 MS_LOG(ERROR) << "SparseToDenseRun error task_id[" << task_id << "] error_code[" << ret << "]";
92 return RET_ERROR;
93 }
94 return RET_OK;
95 }
96
GenerateIndices()97 int SparseToDenseCPUKernel::GenerateIndices() {
98 auto input0 = in_tensors_.at(0);
99 index_num = input0->shape().at(0);
100 if (index_num >= std::numeric_limits<int>::max() / static_cast<int>(sizeof(int *))) {
101 MS_LOG(ERROR) << "Input dim is invalid, dim: " << index_num;
102 return RET_ERROR;
103 }
104 sparse_indices_vect =
105 reinterpret_cast<int **>(ctx_->allocator->Malloc(sizeof(int *) * static_cast<size_t>(index_num)));
106 if (sparse_indices_vect == nullptr) {
107 MS_LOG(ERROR) << "Null pointer reference: sparse_indices_vect.";
108 return RET_ERROR;
109 }
110 index_dim = static_cast<int>(input0->shape().size());
111 int *sparse_indices = reinterpret_cast<int *>(input0->MutableData());
112 CHECK_NULL_RETURN(sparse_indices);
113 switch (index_dim) {
114 case 0:
115 case 1: {
116 for (int i = 0; i < index_num; i++) {
117 sparse_indices_vect[i] = new int[DIMENSION_4D];
118 if (sparse_indices_vect[i] == nullptr) {
119 MS_LOG(ERROR) << "Null pointer reference: sparse_indices_vect[" << i << "].";
120 return RET_ERROR;
121 }
122 for (int j = 0; j < DIMENSION_4D - 1; j++) {
123 sparse_indices_vect[i][j] = 0;
124 }
125 sparse_indices_vect[i][DIMENSION_4D - 1] = sparse_indices[i];
126 }
127 break;
128 }
129 case 2: {
130 int true_dims = input0->shape().at(1);
131 MS_ASSERT(true_dims <= DIMENSION_4D);
132 for (int i = 0; i < index_num; i++) {
133 sparse_indices_vect[i] = new int[DIMENSION_4D];
134 if (sparse_indices_vect[i] == nullptr) {
135 MS_LOG(ERROR) << "Null pointer reference: sparse_indices_vect[" << i << "].";
136 return RET_ERROR;
137 }
138 for (int j = 0; j < DIMENSION_4D - true_dims; j++) {
139 sparse_indices_vect[i][j] = 0;
140 }
141 for (int j = 0; j < true_dims; j++) {
142 sparse_indices_vect[i][j + DIMENSION_4D - true_dims] = sparse_indices[i * true_dims + j];
143 }
144 }
145 break;
146 }
147 default: {
148 MS_LOG(ERROR) << "Indices dimensions is " << index_dim << ", which must be 0, 1 or 2";
149 return RET_ERROR;
150 }
151 }
152 return RET_OK;
153 }
154
IndicesValidCheck() const155 int SparseToDenseCPUKernel::IndicesValidCheck() const {
156 int d1 = output_shape[1] * output_shape[2] * output_shape[3];
157 int d2 = output_shape[2] * output_shape[3];
158 int d3 = output_shape[3];
159 int index_before = -1;
160 for (int i = 0; i < index_num; i++) {
161 int index = d1 * sparse_indices_vect[i][0] + d2 * sparse_indices_vect[i][1] + d3 * sparse_indices_vect[i][2] +
162 sparse_indices_vect[i][3];
163 if (index <= index_before) {
164 return RET_ERROR;
165 }
166 index_before = index;
167 }
168 return RET_OK;
169 }
170
Run()171 int SparseToDenseCPUKernel::Run() {
172 auto ret = GenerateIndices();
173 if (ret != RET_OK) {
174 MS_LOG(ERROR) << "Generate Indices failed.";
175 return RET_ERROR;
176 }
177 if (s2d_param->validate_indices_ == true) {
178 auto ret2 = IndicesValidCheck();
179 if (ret2 != RET_OK) {
180 MS_LOG(ERROR) << "The sparse indices is not valid.";
181 return RET_ERROR;
182 }
183 }
184 output_data = reinterpret_cast<float *>(out_tensors_.at(0)->MutableData());
185 CHECK_NULL_RETURN(output_data);
186 MS_CHECK_FALSE_MSG(thread_count_ == 0, RET_ERROR, "div zero");
187 count_unit_ = thread_count_ > 1 ? UP_DIV(index_num, thread_count_) : index_num;
188 ret = ParallelLaunch(this->ms_context_, SparseToDenseRun, this, s2d_param->thread_num_);
189 if (ret != RET_OK) {
190 MS_LOG(ERROR) << "SparseToDenseRun error: error_code[" << ret << "]";
191 return RET_ERROR;
192 }
193
194 if (sparse_indices_vect != nullptr) {
195 for (int i = 0; i < index_num; i++) {
196 if (sparse_indices_vect[i] != nullptr) {
197 delete[] sparse_indices_vect[i];
198 }
199 }
200 ctx_->allocator->Free(sparse_indices_vect);
201 sparse_indices_vect = nullptr;
202 }
203
204 return RET_OK;
205 }
206
207 REG_KERNEL(kCPU, kNumberTypeFloat32, PrimitiveType_SparseToDense, LiteKernelCreator<SparseToDenseCPUKernel>)
208 REG_KERNEL(kCPU, kNumberTypeInt32, PrimitiveType_SparseToDense, LiteKernelCreator<SparseToDenseCPUKernel>)
209 } // namespace mindspore::kernel
210