• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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