Caffe2 - C++ API
A deep learning, cross platform ML framework
utils.cc
1 #include "caffe2/utils/math/utils.h"
2 
3 #include <algorithm>
4 #include <functional>
5 #include <numeric>
6 #include <vector>
7 
8 #include "caffe2/core/logging.h"
9 
10 namespace caffe2 {
11 namespace math {
12 namespace utils {
13 
14 #define CAFFE2_SPECIALIZED_INCREASE_INDEX_IN_DIMS(TIndex) \
15  template <> \
16  C10_EXPORT void IncreaseIndexInDims<TIndex>( \
17  const int ndim, const TIndex* dims, TIndex* index) { \
18  for (int i = ndim - 1; i >= 0; --i) { \
19  ++index[i]; \
20  if (index[i] >= dims[i]) { \
21  index[i] -= dims[i]; \
22  } else { \
23  break; \
24  } \
25  } \
26  }
27 CAFFE2_SPECIALIZED_INCREASE_INDEX_IN_DIMS(std::int32_t)
28 CAFFE2_SPECIALIZED_INCREASE_INDEX_IN_DIMS(std::int64_t)
29 #undef CAFFE2_SPECIALIZED_INCREASE_INDEX_IN_DIMS
30 
31 int GetIndexFromDims(const int n, const int* dims, const int* index) {
32  int sum = 0;
33  for (int i = 0; i < n; ++i) {
34  if (dims[i] > 1) {
35  sum = sum * dims[i] + index[i];
36  }
37  }
38  return sum;
39 }
40 
41 bool IsIdentityPermutation(const int n, const int* perm) {
42  for (int i = 0; i < n; ++i) {
43  if (perm[i] != i) {
44  return false;
45  }
46  }
47  return true;
48 }
49 
50 bool CheckReduceDims(const int ndim, const int* X_dims, const int* Y_dims) {
51  for (int i = 0; i < ndim; ++i) {
52  if (X_dims[i] != Y_dims[i] && Y_dims[i] != 1) {
53  return false;
54  }
55  }
56  return true;
57 }
58 
59 bool IsRowwiseReduce(
60  const int ndim,
61  const int* A_dims,
62  const int* B_dims,
63  int* rows,
64  int* cols) {
65  *cols = 1;
66  int pivot = ndim - 1;
67  for (; pivot >= 0 && B_dims[pivot] == 1; --pivot) {
68  *cols *= A_dims[pivot];
69  }
70  *rows = 1;
71  for (int i = pivot; i >= 0; --i) {
72  if (A_dims[i] != B_dims[i]) {
73  return false;
74  }
75  *rows *= A_dims[i];
76  }
77  return true;
78 }
79 
80 bool IsColwiseReduce(
81  const int ndim,
82  const int* A_dims,
83  const int* B_dims,
84  int* rows,
85  int* cols) {
86  *rows = 1;
87  int pivot = 0;
88  for (; pivot < ndim && B_dims[pivot] == 1; ++pivot) {
89  *rows *= A_dims[pivot];
90  }
91  *cols = 1;
92  for (int i = pivot; i < ndim; ++i) {
93  if (A_dims[i] != B_dims[i]) {
94  return false;
95  }
96  *cols *= A_dims[i];
97  }
98  return true;
99 }
100 
101 bool IsBothEndsReduce(
102  const int ndim,
103  const int* A_dims,
104  const int* B_dims,
105  int* pre,
106  int* mid,
107  int* nxt) {
108  *nxt = 1;
109  int r = ndim - 1;
110  for (; r >= 0 && B_dims[r] == 1; --r) {
111  *nxt *= A_dims[r];
112  }
113  *pre = 1;
114  int l = 0;
115  for (; l <= r && B_dims[l] == 1; ++l) {
116  *pre *= A_dims[l];
117  }
118  *mid = 1;
119  for (int i = l; i <= r; ++i) {
120  if (A_dims[i] != B_dims[i]) {
121  return false;
122  }
123  *mid *= A_dims[i];
124  }
125  return true;
126 }
127 
128 void ComputeBroadcastBinaryOpDims(
129  const int A_ndim,
130  const int* A_dims,
131  const int B_ndim,
132  const int* B_dims,
133  int* A_broadcast_dims,
134  int* B_broadcast_dims,
135  int* C_broadcast_dims) {
136  const int ndim = std::max(A_ndim, B_ndim);
137  std::fill(A_broadcast_dims, A_broadcast_dims + ndim - A_ndim, 1);
138  std::fill(B_broadcast_dims, B_broadcast_dims + ndim - B_ndim, 1);
139  std::copy(A_dims, A_dims + A_ndim, A_broadcast_dims + ndim - A_ndim);
140  std::copy(B_dims, B_dims + B_ndim, B_broadcast_dims + ndim - B_ndim);
141  for (int i = 0; i < ndim; ++i) {
142  CAFFE_ENFORCE(
143  A_broadcast_dims[i] == B_broadcast_dims[i] ||
144  A_broadcast_dims[i] <= 1 || B_broadcast_dims[i] <= 1);
145  if (A_broadcast_dims[i] == 0 || B_broadcast_dims[i] == 0) {
146  C_broadcast_dims[i] = 0;
147  } else {
148  C_broadcast_dims[i] = std::max(A_broadcast_dims[i], B_broadcast_dims[i]);
149  }
150  }
151 }
152 
153 bool IsRowwiseBroadcastBinaryOp(
154  const int ndim,
155  const int* A_dims,
156  const int* B_dims,
157  int* rows,
158  int* cols,
159  bool* broadcast_1st) {
160  if (ndim == 0) {
161  return false;
162  }
163  int A_pivot = 0;
164  for (; A_pivot < ndim && A_dims[A_pivot] == 1; ++A_pivot)
165  ;
166  int B_pivot = 0;
167  for (; B_pivot < ndim && B_dims[B_pivot] == 1; ++B_pivot)
168  ;
169  if (A_pivot == B_pivot) {
170  return false;
171  }
172  const int pivot = std::max(A_pivot, B_pivot);
173  if (A_pivot > B_pivot) {
174  *rows = std::accumulate(
175  B_dims + B_pivot, B_dims + pivot, 1, std::multiplies<int>());
176  *broadcast_1st = true;
177  } else {
178  *rows = std::accumulate(
179  A_dims + A_pivot, A_dims + pivot, 1, std::multiplies<int>());
180  *broadcast_1st = false;
181  }
182  *cols = 1;
183  for (int i = pivot; i < ndim; ++i) {
184  if (A_dims[i] != B_dims[i]) {
185  return false;
186  }
187  *cols *= A_dims[i];
188  }
189  return true;
190 }
191 
192 bool IsColwiseBroadcastBinaryOp(
193  const int ndim,
194  const int* A_dims,
195  const int* B_dims,
196  int* rows,
197  int* cols,
198  bool* broadcast_1st) {
199  if (ndim == 0) {
200  return false;
201  }
202  int A_pivot = ndim - 1;
203  for (; A_pivot >= 0 && A_dims[A_pivot] == 1; --A_pivot)
204  ;
205  int B_pivot = ndim - 1;
206  for (; B_pivot >= 0 && B_dims[B_pivot] == 1; --B_pivot)
207  ;
208  if (A_pivot == B_pivot) {
209  return false;
210  }
211  ++A_pivot;
212  ++B_pivot;
213  const int pivot = std::min(A_pivot, B_pivot);
214  if (A_pivot < B_pivot) {
215  *cols = std::accumulate(
216  B_dims + pivot, B_dims + B_pivot, 1, std::multiplies<int>());
217  *broadcast_1st = true;
218  } else {
219  *cols = std::accumulate(
220  A_dims + pivot, A_dims + A_pivot, 1, std::multiplies<int>());
221  *broadcast_1st = false;
222  }
223  *rows = 1;
224  for (int i = 0; i < pivot; ++i) {
225  if (A_dims[i] != B_dims[i]) {
226  return false;
227  }
228  *rows *= A_dims[i];
229  }
230  return true;
231 }
232 
233 bool IsBothEndsBroadcastBinaryOp(
234  const int ndim,
235  const int* A_dims,
236  const int* B_dims,
237  int* pre,
238  int* mid,
239  int* nxt,
240  bool* broadcast_1st) {
241  if (ndim == 0) {
242  return false;
243  }
244  int A_pre = 0;
245  for (; A_pre < ndim && A_dims[A_pre] == 1; ++A_pre)
246  ;
247  int B_pre = 0;
248  for (; B_pre < ndim && B_dims[B_pre] == 1; ++B_pre)
249  ;
250  int A_nxt = ndim - 1;
251  for (; A_nxt >= 0 && A_dims[A_nxt] == 1; --A_nxt)
252  ;
253  int B_nxt = ndim - 1;
254  for (; B_nxt >= 0 && B_dims[B_nxt] == 1; --B_nxt)
255  ;
256  ++A_nxt;
257  ++B_nxt;
258  if (A_pre == B_pre || A_nxt == B_nxt) {
259  return false;
260  }
261  if (A_pre > B_pre && A_nxt < B_nxt) {
262  *pre = std::accumulate(
263  B_dims + B_pre, B_dims + A_pre, 1, std::multiplies<int>());
264  *nxt = std::accumulate(
265  B_dims + A_nxt, B_dims + B_nxt, 1, std::multiplies<int>());
266  *broadcast_1st = true;
267  } else if (A_pre < B_pre && A_nxt > B_nxt) {
268  *pre = std::accumulate(
269  A_dims + A_pre, A_dims + B_pre, 1, std::multiplies<int>());
270  *nxt = std::accumulate(
271  A_dims + B_nxt, A_dims + A_nxt, 1, std::multiplies<int>());
272  *broadcast_1st = false;
273  } else {
274  return false;
275  }
276  const int l = std::max(A_pre, B_pre);
277  const int r = std::min(A_nxt, B_nxt);
278  *mid = 1;
279  for (int i = l; i < r; ++i) {
280  if (A_dims[i] != B_dims[i]) {
281  return false;
282  }
283  *mid *= A_dims[i];
284  }
285  return true;
286 }
287 
288 bool IsBatchTranspose2D(const int ndim, const int* axes) {
289  if (ndim < 2) {
290  return false;
291  }
292  for (int i = 0; i < ndim - 2; ++i) {
293  if (axes[i] != i) {
294  return false;
295  }
296  }
297  return axes[ndim - 2] == ndim - 1 && axes[ndim - 1] == ndim - 2;
298 }
299 
300 void ComputeTransposeAxesForReduceOp(
301  const int num_dims,
302  const int num_reduce_axes,
303  const int* reduce_axes,
304  int* transpose_axes) {
305  const int d = num_dims - num_reduce_axes;
306  std::copy_n(reduce_axes, num_reduce_axes, transpose_axes + d);
307  std::sort(transpose_axes + d, transpose_axes + num_dims);
308  int p = 0;
309  int q = d;
310  for (int i = 0; i < num_dims; ++i) {
311  if (q < num_dims && i == transpose_axes[q]) {
312  ++q;
313  } else {
314  transpose_axes[p++] = i;
315  }
316  }
317 }
318 
319 void ComputeTransposeAxesForReduceOp(
320  const int ndim,
321  const int* dims,
322  int* axes) {
323  const int d = ndim - std::count(dims, dims + ndim, 1);
324  int p = 0;
325  int q = d;
326  for (int i = 0; i < ndim; ++i) {
327  if (dims[i] == 1) {
328  axes[q++] = i;
329  } else {
330  axes[p++] = i;
331  }
332  }
333 }
334 
335 #define CAFFE2_SPECIALIZED_COMPUTE_TRANSPOSED_STRIDES(TIndex) \
336  template <> \
337  C10_EXPORT void ComputeTransposedStrides<TIndex>( \
338  const int ndim, const TIndex* dims, const int* axes, TIndex* strides) { \
339  std::vector<TIndex> buff(ndim); \
340  TIndex cur_stride = 1; \
341  for (int i = ndim - 1; i >= 0; --i) { \
342  buff[i] = cur_stride; \
343  cur_stride *= dims[i]; \
344  } \
345  for (int i = 0; i < ndim; ++i) { \
346  strides[i] = buff[axes[i]]; \
347  } \
348  }
349 CAFFE2_SPECIALIZED_COMPUTE_TRANSPOSED_STRIDES(std::int32_t)
350 CAFFE2_SPECIALIZED_COMPUTE_TRANSPOSED_STRIDES(std::int64_t)
351 #undef CAFFE2_SPECIALIZED_COMPUTE_TRANSPOSED_STRIDES
352 
353 } // namespace utils
354 } // namespace math
355 } // namespace caffe2
A global dictionary that holds information about what Caffe2 modules have been loaded in the current ...
Definition: blob.h:13