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