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