1 /* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
2
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6
7 http://www.apache.org/licenses/LICENSE-2.0
8
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15 #include "tensorflow/lite/kernels/internal/utils/sparsity_format_converter.h"
16
17 #include <algorithm>
18 #include <cstdint>
19 #include <utility>
20 #include <vector>
21
22 namespace tflite {
23 namespace internal {
24 namespace sparsity {
25
26 namespace {
GetFlattenedIndex(const std::vector<int> & indices,const std::vector<int> & shape)27 uint64_t GetFlattenedIndex(const std::vector<int>& indices,
28 const std::vector<int>& shape) {
29 uint64_t index = 0;
30 int sub_elements = 1;
31 for (int i = shape.size() - 1; i >= 0; i--) {
32 index += indices[i] * sub_elements;
33 sub_elements *= shape[i];
34 }
35 return index;
36 }
37
TfLiteIntArrayToVector(const TfLiteIntArray * int_array)38 std::vector<int> TfLiteIntArrayToVector(const TfLiteIntArray* int_array) {
39 std::vector<int> values;
40 if (!int_array) {
41 return values;
42 }
43
44 values.resize(int_array->size);
45 for (size_t i = 0; i < int_array->size; i++) {
46 values[i] = int_array->data[i];
47 }
48
49 return values;
50 }
51
52 } // namespace
53
54 template <typename T>
FormatConverter(const std::vector<int> & shape,const std::vector<int> & traversal_order,const std::vector<TfLiteDimensionType> & format,const std::vector<int> & block_size,const std::vector<int> & block_map)55 FormatConverter<T>::FormatConverter(
56 const std::vector<int>& shape, const std::vector<int>& traversal_order,
57 const std::vector<TfLiteDimensionType>& format,
58 const std::vector<int>& block_size, const std::vector<int>& block_map)
59 : dense_shape_(shape),
60 traversal_order_(traversal_order),
61 block_size_(block_size),
62 block_map_(block_map) {
63 dense_size_ = 1;
64 int block_dim = 0;
65 blocked_shape_.resize(shape.size());
66 format_.resize(shape.size() + block_map.size());
67 for (int i = 0; i < shape.size(); i++) {
68 format_[i] = format[traversal_order[i]];
69 dense_size_ *= shape[i];
70 if (block_dim < block_map.size() && block_map[block_dim] == i) {
71 blocked_shape_[i] = shape[i] / block_size[block_dim];
72 block_dim++;
73 } else {
74 blocked_shape_[i] = shape[i];
75 }
76 }
77
78 // Only dense blocks are supported.
79 for (int i = 0; i < block_map.size(); i++) {
80 format_[i + shape.size()] = kTfLiteDimDense;
81 }
82 }
83
84 template <typename T>
DenseToSparse(const T * src_data)85 TfLiteStatus FormatConverter<T>::DenseToSparse(const T* src_data) {
86 int num_original_dims = dense_shape_.size();
87 int num_block_dims = block_map_.size();
88 int num_expanded_dims = num_original_dims + num_block_dims;
89 std::vector<int> expanded_shape(num_expanded_dims);
90 for (int i = 0; i < num_expanded_dims; i++) {
91 if (i < num_original_dims) {
92 expanded_shape[i] = blocked_shape_[i];
93 } else {
94 expanded_shape[i] = block_size_[i - num_original_dims];
95 }
96 }
97
98 std::vector<int> shape_offset(num_original_dims);
99 shape_offset[shape_offset.size() - 1] = 1;
100 for (int i = num_original_dims - 1; i > 0; --i) {
101 shape_offset[i - 1] = shape_offset[i] * dense_shape_[i];
102 }
103
104 std::vector<int> expanded_shape_offset(num_expanded_dims);
105 for (int i = 0; i < num_original_dims; ++i) {
106 expanded_shape_offset[i] = shape_offset[i];
107 }
108 for (int i = 0; i < num_block_dims; ++i) {
109 int mapped_dim = block_map_[i];
110 expanded_shape_offset[num_original_dims + i] = shape_offset[mapped_dim];
111 expanded_shape_offset[mapped_dim] *= block_size_[i];
112 }
113
114 std::vector<int> dst_ordered_offset(num_expanded_dims);
115 for (int i = 0; i < num_expanded_dims; ++i) {
116 dst_ordered_offset[i] = expanded_shape_offset[traversal_order_[i]];
117 }
118
119 std::vector<bool> dst_dim_has_nonzeroes(num_expanded_dims);
120 std::fill(dst_dim_has_nonzeroes.begin(), dst_dim_has_nonzeroes.end(), false);
121 std::vector<int> inner_compressed_dim(num_expanded_dims);
122 int most_recent_compressed_dim = -1;
123 std::vector<int> num_segments_of_next_compressed_dim(num_expanded_dims);
124 int segment_count = 1;
125 for (int i = num_expanded_dims - 1; i >= 0; --i) {
126 inner_compressed_dim[i] = most_recent_compressed_dim;
127 if (format_[i] == kTfLiteDimSparseCSR) {
128 most_recent_compressed_dim = i;
129 num_segments_of_next_compressed_dim[i] = segment_count;
130 segment_count = 1;
131 } else {
132 num_segments_of_next_compressed_dim[i] = -1;
133 segment_count *= expanded_shape[traversal_order_[i]];
134 }
135 }
136
137 dim_metadata_.resize(num_expanded_dims * 2);
138 std::vector<int> dst_sparse_dims;
139 dst_sparse_dims.reserve(num_expanded_dims);
140 for (int i = 0; i < num_expanded_dims; ++i) {
141 dim_metadata_[i * 2].clear();
142 dim_metadata_[i * 2 + 1].clear();
143 if (format_[i] == kTfLiteDimDense) {
144 // If dimension is dense, just store the shape.
145 dim_metadata_[i * 2].push_back(expanded_shape[traversal_order_[i]]);
146 } else {
147 dim_metadata_[i * 2].push_back(0); // Segment array always begins with 0.
148 dst_sparse_dims.push_back(i); // Add dimension to the sparse list.
149 }
150 }
151
152 // This algorithm assumes that the block size is small enough for all the
153 // elements to fit in cache, so the strided accesses from different traversal
154 // order and the write-first-erase-later strategy shouldn't be too slow
155 int dst_dim_idx = num_expanded_dims;
156 std::vector<int> coordinate(num_expanded_dims, 0);
157 int dense_tensor_idx = 0;
158 while (dst_dim_idx >= 0) {
159 if (dst_dim_idx == num_expanded_dims) {
160 // We have a complete coordinate. Add the element to the value array if it
161 // is not zero, or if the last dimension is dense.
162 if (!IsZero(src_data[dense_tensor_idx])) {
163 data_.push_back(src_data[dense_tensor_idx]);
164 // Mark all sparse dimensions that their current indices have nonzeroes.
165 for (auto dst_dim : dst_sparse_dims) {
166 if (!dst_dim_has_nonzeroes[dst_dim]) {
167 // Only add the index to the indices array if the current nonzero
168 // is the first nonzero of the block.
169 dim_metadata_[2 * dst_dim + 1].push_back(coordinate[dst_dim]);
170 dst_dim_has_nonzeroes[dst_dim] = true;
171 }
172 }
173 } else if (format_[num_expanded_dims - 1] == kTfLiteDimDense) {
174 data_.push_back(src_data[dense_tensor_idx]);
175 }
176 --dst_dim_idx;
177 } else {
178 int original_dim_idx = traversal_order_[dst_dim_idx];
179 int dim_size = expanded_shape[original_dim_idx];
180 if (dst_dim_has_nonzeroes[dst_dim_idx]) {
181 // If the previous block has nonzeroes, reset the flag to false since
182 // we have just moved to a new block.
183 dst_dim_has_nonzeroes[dst_dim_idx] = false;
184 } else if (format_[dst_dim_idx] == kTfLiteDimSparseCSR) {
185 // This block is empty. Delete unnecessary values if compressed.
186 int next_compressed_dim = inner_compressed_dim[dst_dim_idx];
187 int erase_offset = dim_metadata_[2 * dst_dim_idx + 1].size() *
188 num_segments_of_next_compressed_dim[dst_dim_idx];
189 if (next_compressed_dim >= 0) {
190 auto& segments = dim_metadata_[2 * inner_compressed_dim[dst_dim_idx]];
191 segments.erase(segments.begin() + 1 + erase_offset, segments.end());
192 } else {
193 data_.erase(data_.begin() + erase_offset, data_.end());
194 }
195 }
196 if (++coordinate[dst_dim_idx] < dim_size) {
197 // The current dst_dim_idx is valid (not out of bound).
198 dense_tensor_idx += dst_ordered_offset[dst_dim_idx];
199 ++dst_dim_idx;
200 } else {
201 // dst_dim_idx has reached its dim size. Update segment array and go
202 // back to incrementing the previous dimension (dst_dim_idx - 1).
203 if (format_[dst_dim_idx] == kTfLiteDimSparseCSR) {
204 dim_metadata_[2 * dst_dim_idx].push_back(
205 dim_metadata_[2 * dst_dim_idx + 1].size());
206 }
207 coordinate[dst_dim_idx] = -1;
208 dense_tensor_idx -= dst_ordered_offset[dst_dim_idx] * dim_size;
209 --dst_dim_idx;
210 }
211 }
212 }
213
214 return kTfLiteOk;
215 }
216
217 template <typename T>
FormatConverter(const std::vector<int> & shape,const std::vector<int> & traversal_order,const std::vector<TfLiteDimensionType> & format,const std::vector<int> & dense_size,const std::vector<std::vector<int>> & segments,const std::vector<std::vector<int>> & indices,const std::vector<int> & block_map)218 FormatConverter<T>::FormatConverter(
219 const std::vector<int>& shape, const std::vector<int>& traversal_order,
220 const std::vector<TfLiteDimensionType>& format,
221 const std::vector<int>& dense_size,
222 const std::vector<std::vector<int>>& segments,
223 const std::vector<std::vector<int>>& indices,
224 const std::vector<int>& block_map) {
225 InitSparseToDenseConverter(shape, traversal_order, format, dense_size,
226 segments, indices, block_map);
227 }
228
229 template <typename T>
FormatConverter(const std::vector<int> & shape,const TfLiteSparsity & sparsity)230 FormatConverter<T>::FormatConverter(const std::vector<int>& shape,
231 const TfLiteSparsity& sparsity) {
232 auto traversal_order = TfLiteIntArrayToVector(sparsity.traversal_order);
233 auto block_map = TfLiteIntArrayToVector(sparsity.block_map);
234
235 std::vector<TfLiteDimensionType> format(sparsity.dim_metadata_size);
236 std::vector<int> dense_size(sparsity.dim_metadata_size);
237 std::vector<std::vector<int>> segments(sparsity.dim_metadata_size);
238 std::vector<std::vector<int>> indices(sparsity.dim_metadata_size);
239 for (int i = 0; i < sparsity.dim_metadata_size; i++) {
240 format[i] = sparsity.dim_metadata[i].format;
241 dense_size[i] = sparsity.dim_metadata[i].dense_size;
242 segments[i] =
243 TfLiteIntArrayToVector(sparsity.dim_metadata[i].array_segments);
244 indices[i] = TfLiteIntArrayToVector(sparsity.dim_metadata[i].array_indices);
245 }
246
247 InitSparseToDenseConverter(shape, std::move(traversal_order),
248 std::move(format), std::move(dense_size),
249 std::move(segments), std::move(indices),
250 std::move(block_map));
251 }
252
253 template <typename T>
InitSparseToDenseConverter(std::vector<int> shape,std::vector<int> traversal_order,std::vector<TfLiteDimensionType> format,std::vector<int> dense_size,std::vector<std::vector<int>> segments,std::vector<std::vector<int>> indices,std::vector<int> block_map)254 void FormatConverter<T>::InitSparseToDenseConverter(
255 std::vector<int> shape, std::vector<int> traversal_order,
256 std::vector<TfLiteDimensionType> format, std::vector<int> dense_size,
257 std::vector<std::vector<int>> segments,
258 std::vector<std::vector<int>> indices, std::vector<int> block_map) {
259 dense_shape_ = std::move(shape);
260 traversal_order_ = std::move(traversal_order);
261 block_map_ = std::move(block_map);
262 format_ = std::move(format);
263
264 dense_size_ = 1;
265 for (int i = 0; i < dense_shape_.size(); i++) {
266 dense_size_ *= dense_shape_[i];
267 }
268
269 dim_metadata_.resize(2 * format_.size());
270 for (int i = 0; i < format_.size(); i++) {
271 if (format_[i] == kTfLiteDimDense) {
272 dim_metadata_[2 * i] = {dense_size[i]};
273 } else {
274 dim_metadata_[2 * i] = std::move(segments[i]);
275 dim_metadata_[2 * i + 1] = std::move(indices[i]);
276 }
277 }
278
279 int original_rank = dense_shape_.size();
280 int block_dim = 0;
281
282 blocked_shape_.resize(original_rank);
283 block_size_.resize(block_map_.size());
284 for (int i = 0; i < original_rank; i++) {
285 if (block_dim < block_map_.size() && block_map_[block_dim] == i) {
286 if (original_rank + block_dim < traversal_order_.size()) {
287 int orig_dim = traversal_order_[original_rank + block_dim];
288 block_size_[block_dim] = dense_size[orig_dim];
289 blocked_shape_[i] = dense_shape_[i] / dense_size[orig_dim];
290 block_dim++;
291 }
292 } else {
293 blocked_shape_[i] = dense_shape_[i];
294 }
295 }
296 }
297
298 template <typename T>
Populate(const T * src_data,std::vector<int> indices,int level,int prev_idx,int * src_data_ptr,T * dest_data)299 void FormatConverter<T>::Populate(const T* src_data, std::vector<int> indices,
300 int level, int prev_idx, int* src_data_ptr,
301 T* dest_data) {
302 if (level == indices.size()) {
303 int orig_rank = dense_shape_.size();
304 std::vector<int> orig_idx;
305 orig_idx.resize(orig_rank);
306 int i = 0;
307 for (; i < orig_idx.size(); i++) {
308 int orig_dim = traversal_order_[i];
309 orig_idx[orig_dim] = indices[i];
310 }
311
312 for (; i < indices.size(); i++) {
313 const int block_idx = traversal_order_[i] - orig_rank;
314 const int orig_dim = block_map_[block_idx];
315 orig_idx[orig_dim] =
316 orig_idx[orig_dim] * block_size_[block_idx] + indices[i];
317 }
318
319 dest_data[GetFlattenedIndex(orig_idx, dense_shape_)] =
320 src_data[*src_data_ptr];
321
322 *src_data_ptr = *src_data_ptr + 1;
323 return;
324 }
325
326 const int metadata_idx = 2 * level;
327 const int shape_of_level = dim_metadata_[metadata_idx][0];
328 if (format_[level] == kTfLiteDimDense) {
329 for (int i = 0; i < shape_of_level; i++) {
330 indices[level] = i;
331 Populate(src_data, indices, level + 1, prev_idx * shape_of_level + i,
332 src_data_ptr, dest_data);
333 }
334 } else if (prev_idx + 1 < dim_metadata_[metadata_idx].size()) {
335 const auto& array_segments = dim_metadata_[metadata_idx];
336 const auto& array_indices = dim_metadata_[metadata_idx + 1];
337 for (int i = array_segments[prev_idx]; i < array_segments[prev_idx + 1];
338 i++) {
339 if (i < array_indices.size() && level < indices.size()) {
340 indices[level] = array_indices[i];
341 Populate(src_data, indices, level + 1, i, src_data_ptr, dest_data);
342 }
343 }
344 }
345 }
346
347 template <typename T>
SparseToDense(const T * src_data)348 TfLiteStatus FormatConverter<T>::SparseToDense(const T* src_data) {
349 data_.resize(dense_size_);
350 std::fill(data_.begin(), data_.end(), T(0));
351
352 int total_rank = traversal_order_.size();
353 int src_data_ptr = 0;
354 std::vector<int> indices(total_rank);
355 Populate(src_data, indices, 0, 0, &src_data_ptr, data_.data());
356
357 return kTfLiteOk;
358 }
359
360 template <typename T>
SparseToDense(const T * src_data,const size_t dest_size,T * dest_data,TfLiteContext * context)361 TfLiteStatus FormatConverter<T>::SparseToDense(const T* src_data,
362 const size_t dest_size,
363 T* dest_data,
364 TfLiteContext* context) {
365 if (dest_size != dense_size_) {
366 TF_LITE_MAYBE_KERNEL_LOG(
367 context, "unexpected buffer size for densified data, expected %lld.\n",
368 dense_size_);
369 return kTfLiteError;
370 }
371
372 // For types like Eigen::half, we cannot do a simple memset() with 0 values.
373 for (auto i = 0; i < dest_size; i++) {
374 dest_data[i] = T(0);
375 }
376
377 const int total_rank = traversal_order_.size();
378 int src_data_ptr = 0;
379 std::vector<int> indices(total_rank);
380 Populate(src_data, indices, 0, 0, &src_data_ptr, dest_data);
381
382 return kTfLiteOk;
383 }
384
385 template <typename T>
IsZero(const T val)386 bool FormatConverter<T>::IsZero(const T val) {
387 return (val == static_cast<T>(0));
388 }
389
390 template class FormatConverter<int32_t>;
391 template class FormatConverter<int8_t>;
392 template class FormatConverter<float>;
393 template class FormatConverter<Eigen::half>;
394
395 } // namespace sparsity
396 } // namespace internal
397 } // namespace tflite
398