nnet-analyze.cc
Go to the documentation of this file.
1 // nnet3/nnet-analyze.cc
2 
3 // Copyright 2015 Johns Hopkins University (author: Daniel Povey)
4 
5 // See ../../COPYING for clarification regarding multiple authors
6 //
7 // Licensed under the Apache License, Version 2.0 (the "License");
8 // you may not use this file except in compliance with the License.
9 // You may obtain a copy of the License at
10 //
11 // http://www.apache.org/licenses/LICENSE-2.0
12 //
13 // THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 // KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
15 // WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
16 // MERCHANTABLITY OR NON-INFRINGEMENT.
17 // See the Apache 2 License for the specific language governing permissions and
18 // limitations under the License.
19 
20 #include "nnet3/nnet-analyze.h"
21 
22 namespace kaldi {
23 namespace nnet3 {
24 
26  const NnetComputation &computation) {
27  // note, these numbers are only valid if you include the empty zero-indexed
28  // matrix/submatrix as a matrix.
29  int32 num_matrices = computation.matrices.size(),
30  num_submatrices = computation.submatrices.size();
31  row_split_points_.resize(num_matrices);
32  column_split_points_.resize(num_matrices);
33  KALDI_ASSERT(computation.submatrices[0].num_rows == 0);
34  for (int32 submatrix_index = 1;
35  submatrix_index < num_submatrices;
36  submatrix_index++) {
38  computation.submatrices[submatrix_index];
43  }
44  for (int32 matrix_index = 1; matrix_index < num_matrices; matrix_index++) {
45  // Because it's possible for matrices not to have any submatrices (after
46  // pruning), we need to make sure that the beginning and end dimensions are
47  // in the split points.
48  column_split_points_[matrix_index].push_back(0);
49  column_split_points_[matrix_index].push_back(
50  computation.matrices[matrix_index].num_cols);
51  row_split_points_[matrix_index].push_back(0);
52  row_split_points_[matrix_index].push_back(
53  computation.matrices[matrix_index].num_rows);
54  SortAndUniq(&(column_split_points_[matrix_index]));
55  SortAndUniq(&(row_split_points_[matrix_index]));
56  }
57  // note: the last split point of each matrix doesn't get its own variable index.
58  matrix_to_variable_index_.resize(num_matrices + 1);
61  for (int32 matrix_index = 1; matrix_index < num_matrices; matrix_index++) {
62  int32 num_row_variables = row_split_points_[matrix_index].size() - 1,
63  num_column_variables = column_split_points_[matrix_index].size() - 1,
64  num_variables = num_row_variables * num_column_variables;
65  KALDI_ASSERT(num_variables >= 1);
66  matrix_to_variable_index_[matrix_index+1] =
67  matrix_to_variable_index_[matrix_index] + num_variables;
68  }
70 }
71 
72 //static
73 int32 ComputationVariables::FindIndexOf(const std::vector<int32> &vec, int32 i) {
74  // std::lower_bound does a binary search -> faster than std::find.
75  std::vector<int32>::const_iterator iter = std::lower_bound(
76  vec.begin(), vec.end(), i);
77  KALDI_ASSERT(*iter == i);
78  return iter - vec.begin();
79 }
80 
82  const NnetComputation &computation) {
83  // note, these numbers are only valid if you include the empty zero-indexed
84  // matrix/submatrix as a matrix.
85  int32 num_submatrices = computation.submatrices.size();
86 
87  variables_for_submatrix_.resize(num_submatrices);
88 
89  submatrix_is_whole_matrix_.resize(num_submatrices, false);
90  submatrix_to_matrix_.resize(num_submatrices);
91  submatrix_to_matrix_[0] = 0;
92 
93  for (int32 submatrix_index = 1;
94  submatrix_index < num_submatrices;
95  submatrix_index++) {
97  computation.submatrices[submatrix_index];
98  int32 matrix_index = s.matrix_index;
99  submatrix_to_matrix_[submatrix_index] = matrix_index;
100  int32 start_col = s.col_offset, end_col = start_col + s.num_cols,
101  start_row = s.row_offset, end_row = start_row + s.num_rows;
102  int32 row_start = FindIndexOf(row_split_points_[matrix_index], start_row),
103  row_end = FindIndexOf(row_split_points_[matrix_index], end_row),
104  col_start = FindIndexOf(column_split_points_[matrix_index], start_col),
105  col_end = FindIndexOf(column_split_points_[matrix_index], end_col),
106  num_column_variables = column_split_points_[matrix_index].size() - 1,
107  num_row_variables = row_split_points_[matrix_index].size() - 1,
108  matrix_start_variable = matrix_to_variable_index_[matrix_index];
109  KALDI_ASSERT(row_end > row_start && col_end > col_start &&
110  col_end <= num_column_variables);
111  std::vector<int32> &variables = variables_for_submatrix_[submatrix_index];
112  for (int32 r = row_start; r < row_end; r++)
113  for (int32 c = col_start; c < col_end; c++)
114  variables.push_back(matrix_start_variable + r*num_column_variables + c);
115  if (row_start == 0 && row_end == num_row_variables &&
116  col_start == 0 && col_end == num_column_variables)
117  submatrix_is_whole_matrix_[submatrix_index] = true;
118  }
119 }
120 
122  variable_to_matrix_.clear();
124  int32 num_matrices = matrix_to_variable_index_.size() - 1;
125  for (int32 matrix_index = 1; matrix_index < num_matrices; matrix_index++) {
126  int32 start_variable = matrix_to_variable_index_[matrix_index],
127  end_variable = matrix_to_variable_index_[matrix_index + 1];
128  for (int32 i = start_variable; i < end_variable; i++)
129  variable_to_matrix_[i] = matrix_index;
130  }
131 }
132 
133 void ComputationVariables::Init(const NnetComputation &computation) {
134  // don't call this twice on the same object..
136  ComputeSplitPoints(computation);
137  ComputeVariablesForSubmatrix(computation);
139 }
140 
142  KALDI_ASSERT(static_cast<size_t>(variable) < variable_to_matrix_.size());
143  return variable_to_matrix_[variable];
144 }
145 
147  int32 submatrix_index,
148  std::vector<int32> *variable_indexes) const {
149  KALDI_ASSERT(static_cast<size_t>(submatrix_index) <
150  variables_for_submatrix_.size());
151  variable_indexes->insert(variable_indexes->end(),
152  variables_for_submatrix_[submatrix_index].begin(),
153  variables_for_submatrix_[submatrix_index].end());
154 }
155 
157  int32 matrix_index,
158  std::vector<int32> *variable_indexes) const {
159  KALDI_ASSERT(static_cast<size_t>(matrix_index + 1) <
161  int32 start = matrix_to_variable_index_[matrix_index],
162  end = matrix_to_variable_index_[matrix_index + 1];
163  variable_indexes->reserve(variable_indexes->size() + end - start);
164  for (int32 variable_index = start; variable_index < end; variable_index++)
165  variable_indexes->push_back(variable_index);
166 }
167 
169  int32 submatrix_index,
170  AccessType access_type,
171  CommandAttributes *ca) const {
172  if (submatrix_index == 0)
173  return;
174  KALDI_ASSERT(static_cast<size_t>(submatrix_index) <
175  submatrix_to_matrix_.size());
176  int32 matrix_index = submatrix_to_matrix_[submatrix_index];
177  bool is_whole_matrix = submatrix_is_whole_matrix_[submatrix_index];
178  switch (access_type) {
179  case kReadAccess:
180  AppendVariablesForSubmatrix(submatrix_index,
181  &(ca->variables_read));
182  ca->matrices_read.push_back(matrix_index);
183  ca->submatrices_read.push_back(submatrix_index);
184  break;
185  case kWriteAccess:
186  AppendVariablesForSubmatrix(submatrix_index,
187  &(ca->variables_written));
188  ca->submatrices_written.push_back(submatrix_index);
189  ca->matrices_written.push_back(matrix_index);
190  // if submatrix does not span the full row range of the matrix,
191  // a write operation has to be considered a read/write operation
192  // on the underlying matrix
193  if (!is_whole_matrix)
194  ca->matrices_read.push_back(matrix_index);
195  break;
196  case kReadWriteAccess:
197  AppendVariablesForSubmatrix(submatrix_index,
198  &(ca->variables_written));
199  AppendVariablesForSubmatrix(submatrix_index,
200  &(ca->variables_read));
201  ca->submatrices_written.push_back(submatrix_index);
202  ca->submatrices_read.push_back(submatrix_index);
203  ca->matrices_written.push_back(matrix_index);
204  ca->matrices_read.push_back(matrix_index);
205  }
206 }
207 
208 std::string ComputationVariables::DescribeVariable(int32 variable) const {
209  KALDI_ASSERT(variable >= 0 && variable < num_variables_);
210  int32 matrix_index = variable_to_matrix_[variable],
211  offset = variable - matrix_to_variable_index_[matrix_index],
212  num_column_variables = column_split_points_[matrix_index].size() - 1,
213  num_row_variables = row_split_points_[matrix_index].size() - 1,
214  column_variable = offset % num_column_variables,
215  row_variable = offset / num_column_variables;
216  KALDI_ASSERT(column_variable >= 0 && row_variable >= 0 &&
217  row_variable < num_row_variables &&
218  column_variable < num_column_variables);
219  std::ostringstream os;
220  os << 'm' << matrix_index;
221  if (num_row_variables != 1 || num_column_variables != 1) {
222  os << '(';
223  if (num_row_variables == 1) {
224  os << ':';
225  } else {
226  os << row_split_points_[matrix_index][row_variable] << ':'
227  << row_split_points_[matrix_index][row_variable+1] - 1;
228  }
229  os << ',';
230  if (num_column_variables == 1) {
231  os << ':';
232  } else {
233  os << column_split_points_[matrix_index][column_variable] << ':'
234  << column_split_points_[matrix_index][column_variable+1] - 1;
235  }
236  os << ')';
237  }
238  return os.str();
239 }
240 
242  int32 variable) const {
243  KALDI_ASSERT(variable >= 0 && variable < num_variables_);
244  int32 matrix_index = variable_to_matrix_[variable],
245  offset = variable - matrix_to_variable_index_[matrix_index],
246  num_column_variables = column_split_points_[matrix_index].size() - 1,
247  column_variable = offset % num_column_variables,
248  row_variable = offset / num_column_variables;
249  int32 row_offset = row_split_points_[matrix_index][row_variable],
250  num_rows = row_split_points_[matrix_index][row_variable+1] - row_offset,
251  col_offset = column_split_points_[matrix_index][column_variable],
252  num_cols = column_split_points_[matrix_index][column_variable+1] -
253  col_offset;
254  return NnetComputation::SubMatrixInfo(matrix_index, row_offset, num_rows,
255  col_offset, num_cols);
256 }
257 
258 
265  const std::vector<std::pair<int32, int32> > &indexes_multi,
266  std::vector<int32> *submatrix_indexes) {
267  submatrix_indexes->clear();
268  std::vector<std::pair<int32, int32> >::const_iterator
269  iter = indexes_multi.begin(), end = indexes_multi.end();
270  int32 cur_submatrix_index = -1; // an optimization.
271  for (; iter != end; ++iter) {
272  int32 submatrix_index = iter->first;
273  if (submatrix_index != -1 && submatrix_index != cur_submatrix_index) {
274  cur_submatrix_index = submatrix_index;
275  submatrix_indexes->push_back(submatrix_index);
276  }
277  }
278  SortAndUniq(submatrix_indexes);
279 }
280 
281 
282 
283 
285  const Nnet &nnet,
286  const NnetComputation &computation,
287  const ComputationVariables &vars,
288  std::vector<CommandAttributes> *attributes) {
289  int32 num_commands = computation.commands.size();
290  attributes->clear();
291  attributes->resize(num_commands);
292  for (int32 command_index = 0; command_index < num_commands; command_index++) {
293  const NnetComputation::Command &c = computation.commands[command_index];
294  CommandAttributes &attr = (*attributes)[command_index];
295  switch (c.command_type) {
296  case kAllocMatrix:
297  case kDeallocMatrix:
298  case kSwapMatrix:
299  break; // the commands above leave the matrix undefined.
300  case kSetConst:
302  break;
303  case kPropagate:
304  vars.RecordAccessForSubmatrix(c.arg3, kReadAccess, &attr);
305  if (nnet.GetComponent(c.arg1)->Properties() & kPropagateAdds)
307  else
309  break;
310  case kBackprop:
312  vars.RecordAccessForSubmatrix(c.arg3, kReadAccess, &attr);
313  vars.RecordAccessForSubmatrix(c.arg4, kReadAccess, &attr);
314  vars.RecordAccessForSubmatrix(c.arg5, kReadAccess, &attr);
315  if (nnet.GetComponent(c.arg1)->Properties() & kBackpropAdds)
317  else
319  if (c.command_type == kBackprop &&
321  attr.has_side_effects = true;
322  break;
323  case kMatrixCopy:
325  vars.RecordAccessForSubmatrix(c.arg2, kReadAccess, &attr);
326  break;
327  case kMatrixAdd:
329  vars.RecordAccessForSubmatrix(c.arg2, kReadAccess, &attr);
330  break;
331  case kAddRows:
333  vars.RecordAccessForSubmatrix(c.arg2, kReadAccess, &attr);
334  break;
335  case kCopyRows: {
336  const std::vector<int32> &indexes = computation.indexes[c.arg3];
337  // if there are -1's in "indexes", then the result of the operation
338  // will depend on the initial value of the matrix, so it's
339  // a "rw" operation, not a "write" operation.
340  if (std::count(indexes.begin(), indexes.end(), -1) > 0)
342  else
344  vars.RecordAccessForSubmatrix(c.arg2, kReadAccess, &attr);
345  break;
346  }
347  case kAddRowsMulti: {
349  std::vector<int32> submatrix_indexes;
351  &submatrix_indexes);
352  for (size_t i = 0; i < submatrix_indexes.size(); i++)
353  vars.RecordAccessForSubmatrix(submatrix_indexes[i],
354  kReadAccess, &attr);
355  break;
356  }
357  case kCopyRowsMulti: {
358  std::vector<int32> submatrix_indexes;
360  &submatrix_indexes);
361  // note: the CopyRows command assigns zero in cases where
362  // there is no source for some row
364  for (size_t i = 0; i < submatrix_indexes.size(); i++)
365  vars.RecordAccessForSubmatrix(submatrix_indexes[i],
366  kReadAccess, &attr);
367  break;
368  }
369  case kAddToRowsMulti:
370  case kCopyToRowsMulti: {
371  vars.RecordAccessForSubmatrix(c.arg1, kReadAccess, &attr);
372  // if the submatrixes we're writing to (in kCopyToRowsMulti) had all
373  // rows covered, it would be a pure write operation.
374  std::vector<int32> submatrix_indexes;
376  &submatrix_indexes);
377  for (size_t i = 0; i < submatrix_indexes.size(); i++)
378  vars.RecordAccessForSubmatrix(submatrix_indexes[i], kReadWriteAccess,
379  &attr);
380  break;
381  }
382  case kAddRowRanges: {
384  vars.RecordAccessForSubmatrix(c.arg2, kReadAccess, &attr);
385  break;
386  }
387  case kCompressMatrix: {
389  break;
390  }
391  case kDecompressMatrix: {
393  break;
394  }
395  case kAcceptInput: {
397  break;
398  }
399  case kProvideOutput: {
400  vars.RecordAccessForSubmatrix(c.arg1, kReadAccess, &attr);
401  break;
402  }
403  case kNoOperation:
405  case kNoOperationMarker:
406  case kNoOperationLabel:
407  case kGotoLabel:
408  break;
409  default:
410  KALDI_ERR << "Unknown command type.";
411  }
416  SortAndUniq(&attr.matrices_read);
418  }
419 }
420 
422  const ComputationVariables &variables,
423  const std::vector<CommandAttributes> &command_attributes,
424  std::vector<std::vector<Access> > *variable_accesses) {
425  int32 num_variables = variables.NumVariables(),
426  num_commands = command_attributes.size();
427  variable_accesses->clear();
428  variable_accesses->resize(num_variables);
429  for (int32 c = 0; c < num_commands; c++) {
430  const CommandAttributes &attr = command_attributes[c];
433  std::vector<int32> all_variables;
434  all_variables.reserve(attr.variables_read.size() +
435  attr.variables_written.size());
436  all_variables.insert(all_variables.end(), attr.variables_read.begin(),
437  attr.variables_read.end());
438  all_variables.insert(all_variables.end(), attr.variables_written.begin(),
439  attr.variables_written.end());
440  SortAndUniq(&all_variables);
441 
442  std::vector<int32>::const_iterator iter = all_variables.begin(),
443  end = all_variables.end();
444  for (; iter != end; ++iter) {
445  int32 variable_index = *iter;
446  bool is_read = std::binary_search(attr.variables_read.begin(),
447  attr.variables_read.end(),
448  variable_index),
449  is_written = (!is_read ? true :
450  std::binary_search(attr.variables_written.begin(),
451  attr.variables_written.end(),
452  variable_index));
453  if (is_read && is_written) {
454  (*variable_accesses)[variable_index].push_back(
456  } else if (is_read) {
457  (*variable_accesses)[variable_index].push_back(
458  Access(c, kReadAccess));
459  } else {
460  (*variable_accesses)[variable_index].push_back(
461  Access(c, kWriteAccess));
462  }
463  }
464  }
465 }
466 
468  const Nnet &nnet,
469  const NnetComputation &computation,
470  const ComputationVariables &variables,
471  const std::vector<CommandAttributes> &command_attributes,
472  std::vector<MatrixAccesses> *matrix_accesses) {
473  int32 num_matrices = computation.matrices.size(),
474  num_commands = command_attributes.size();
475  matrix_accesses->clear();
476  matrix_accesses->resize(num_matrices);
477  for (int32 c = 0; c < num_commands; c++) {
478  const CommandAttributes &attr = command_attributes[c];
481  std::vector<int32> all_matrices;
482  all_matrices.reserve(attr.matrices_read.size() +
483  attr.matrices_written.size());
484  all_matrices.insert(all_matrices.end(), attr.matrices_read.begin(),
485  attr.matrices_read.end());
486  all_matrices.insert(all_matrices.end(), attr.matrices_written.begin(),
487  attr.matrices_written.end());
488  SortAndUniq(&all_matrices);
489 
490  std::vector<int32>::const_iterator iter = all_matrices.begin(),
491  end = all_matrices.end();
492  for (; iter != end; ++iter) {
493  int32 matrix_index = *iter;
494  bool is_read = std::binary_search(attr.matrices_read.begin(),
495  attr.matrices_read.end(),
496  matrix_index),
497  is_written = (!is_read ? true :
498  std::binary_search(attr.matrices_written.begin(),
499  attr.matrices_written.end(),
500  matrix_index));
501  if (is_read && is_written) {
502  (*matrix_accesses)[matrix_index].accesses.push_back(
504  } else if (is_read) {
505  (*matrix_accesses)[matrix_index].accesses.push_back(
506  Access(c, kReadAccess));
507  } else {
508  (*matrix_accesses)[matrix_index].accesses.push_back(
509  Access(c, kWriteAccess));
510  }
511  }
512  // Now set up allocate_command, deallocate_command,
513  // is_input and is_output.
514  const NnetComputation::Command &command = computation.commands[c];
515  int32 matrix_index1, matrix_index2;
516 
517  switch (command.command_type) {
518  case kAllocMatrix:
519  if (!computation.IsWholeMatrix(command.arg1))
520  KALDI_ERR << "Command does not operate on whole matrix";
521  matrix_index1 = computation.submatrices[command.arg1].matrix_index;
522  if ((*matrix_accesses)[matrix_index1].allocate_command != -1)
523  KALDI_ERR << "Matrix " << matrix_index1 << " initialized twice.";
524  (*matrix_accesses)[matrix_index1].allocate_command = c;
525  break;
526  case kSwapMatrix:
527  if (!computation.IsWholeMatrix(command.arg1))
528  KALDI_ERR << "Command does not operate on whole matrix";
529  matrix_index1 = computation.submatrices[command.arg1].matrix_index;
530  KALDI_ASSERT(computation.IsWholeMatrix(command.arg2));
531  matrix_index2 = computation.submatrices[command.arg2].matrix_index;
532  if ((*matrix_accesses)[matrix_index1].allocate_command != -1)
533  KALDI_ERR << "Matrix " << matrix_index1 << " initialized twice.";
534  (*matrix_accesses)[matrix_index1].allocate_command = c;
535  if ((*matrix_accesses)[matrix_index2].deallocate_command != -1)
536  KALDI_ERR << "Matrix " << matrix_index2 << " destroyed twice.";
537  (*matrix_accesses)[matrix_index2].deallocate_command = c;
538  break;
539  case kDeallocMatrix:
540  if (!computation.IsWholeMatrix(command.arg1))
541  KALDI_ERR << "Command does not operate on whole matrix";
542  matrix_index1 = computation.submatrices[command.arg1].matrix_index;
543  if ((*matrix_accesses)[matrix_index1].deallocate_command != -1)
544  KALDI_ERR << "Matrix " << matrix_index1 << " destroyed twice.";
545  (*matrix_accesses)[matrix_index1].deallocate_command = c;
546  break;
547  case kAcceptInput:
548  if (!computation.IsWholeMatrix(command.arg1))
549  KALDI_ERR << "Command does not operate on whole matrix";
550  matrix_index1 = computation.submatrices[command.arg1].matrix_index;
551  (*matrix_accesses)[matrix_index1].is_input = true;
552  // If a certain matrix is accepted as input multiple times, we
553  // count the first one as allocating it (the second will just
554  // allocate it again, which is harmless).
555  if ((*matrix_accesses)[matrix_index1].allocate_command == -1)
556  (*matrix_accesses)[matrix_index1].allocate_command = c;
557  break;
558  case kProvideOutput:
559  if (!computation.IsWholeMatrix(command.arg1))
560  KALDI_ERR << "Command does not operate on whole matrix";
561  matrix_index1 = computation.submatrices[command.arg1].matrix_index;
562  (*matrix_accesses)[matrix_index1].is_output = true;
563  break;
564  default:
565  ;
566  }
567  }
568 }
569 
570 
572  const CheckComputationOptions &config,
573  const Nnet &nnet,
574  const NnetComputation &computation):
575  config_(config), nnet_(nnet), computation_(computation) { }
576 
577 
578 
588 }
589 
590 
599  int32 num_variables = a_.variable_accesses.size();
600  for (int32 v = 0; v < num_variables; v++) {
601  const std::vector<Access> &accesses = a_.variable_accesses[v];
602  if (accesses.empty()) {
604  KALDI_ERR << "Variable " << v << " = " << a_.variables.DescribeVariable(v)
605  << " is never used.";
606  } else {
607  continue;
608  }
609  }
610  int32 num_accesses = accesses.size();
611  int32 first_pure_read = -1;
612  for (int32 access = 0; access < num_accesses; access++) {
613  if (accesses[access].access_type == kReadAccess) {
614  first_pure_read = access;
615  break;
616  }
617  }
618  if (first_pure_read != -1) {
619  for (int32 access = first_pure_read + 1;
620  access < num_accesses; access++) {
621  if (accesses[access].access_type != kReadAccess) {
622  KALDI_ERR << "Variable " << v << " = "
624  << " is modified after being read"
625  << " (this is not expected before optimization)";
626  }
627  }
628  }
629  }
630 }
631 
632 
637  // the variable 'min_proportion' needs to be <= the min_proportion_ value in
638  // class MatrixExtender, otherwise this code could spuriously reject a
639  // computation.
640  BaseFloat min_proportion = 0.8;
641 
642  int32 num_variables = a_.variable_accesses.size();
643  for (int32 v = 0; v < num_variables; v++) {
644  const std::vector<Access> &accesses = a_.variable_accesses[v];
645  if (accesses.empty()) {
648  const NnetComputation::MatrixInfo &matrix_info =
650  // Before we throw an error, we want to check that it isn't a case that
651  // can be produced by the ExtendMatrices() optimization, that is
652  // actually allowed. This is a case when a variable is inside the last
653  // few rows of a matrix, but not all columns of those last rows.
654  if (info.row_offset >= min_proportion * matrix_info.num_rows &&
655  !(info.col_offset == 0 && info.num_cols == matrix_info.num_cols)) {
656  continue;
657  }
658  KALDI_ERR << "Variable " << v << " == "
659  << a_.variables.DescribeVariable(v) << " is never used.";
660  }
661  } else {
662  // It's OK if part of a matrix is compressed, that is undefined;
663  // likely that part won't be referred to when we uncompress.
664  if (accesses[0].access_type != kWriteAccess &&
665  !(computation_.commands[accesses[0].command_index].command_type ==
667  KALDI_ERR << "Variable " << v << " == "
669  << " is read before it is written to";
670  }
671  }
672 }
673 
674 
681 
683  int32 num_matrices = a_.matrix_accesses.size();
684 
685  for (int32 matrix_index = 1; matrix_index < num_matrices; matrix_index++) {
686  const MatrixAccesses &accesses = a_.matrix_accesses[matrix_index];
687  if (accesses.allocate_command == -1)
688  KALDI_ERR << "Matrix m" << matrix_index << " is not initialized.";
689  if (accesses.accesses.empty()) {
690  KALDI_ERR << "Matrix m" << matrix_index << " is never accessed.";
691  } else if (accesses.accesses.front().command_index <
692  accesses.allocate_command) {
693  KALDI_ERR << "Matrix m" << matrix_index << " is accessed before "
694  "it is initialized";
695  }
696  if (accesses.accesses.size() == 1 && config_.check_unused_variables) {
697  int32 first_access_command = accesses.accesses[0].command_index;
698  if (computation_.commands[first_access_command].command_type == kSetConst) {
700  KALDI_ERR << "Matrix m" << matrix_index << " is only set to a constant "
701  << "value, but then never accessed.";
702  }
703  }
704 
705  if (accesses.accesses.empty()) {
706  if (accesses.is_input) {
707  // we allow there to be no accesses if it is an input, e.g. if an
708  // output derivative is supplied for some reason but never used.
709  // We'll warn, though (once).
710  if (!computation_checker_warned_unused_input) {
711  KALDI_WARN << "Matrix m" << matrix_index << " is never accessed. "
712  "Allowing because it is an input (un-needed input or "
713  "derivative?) Will warn only once.";
714  computation_checker_warned_unused_input = true;
715  }
716  } else {
717  KALDI_ERR << "Matrix m" << matrix_index << " is never accessed.";
718  }
719  } else if (accesses.deallocate_command != -1 &&
720  accesses.accesses.back().command_index >=
721  accesses.deallocate_command) {
722  KALDI_ERR << "Matrix m" << matrix_index << " is accessed after "
723  "it is destroyed";
724  }
725  }
726 }
727 
729  int32 num_matrices = a_.matrix_accesses.size();
730 
731  // 'middle_command' will be the index of the command that separates
732  // the forward and backward passes.
733  int32 middle_command = -1;
734  for (size_t i = 0; i < computation_.commands.size(); i++) {
735  if (computation_.commands[i].command_type == kNoOperationMarker) {
736  middle_command = static_cast<int32>(i);
737  break;
738  }
739  }
740  for (int32 matrix_index = 1; matrix_index < num_matrices; matrix_index++) {
741  const MatrixAccesses &accesses = a_.matrix_accesses[matrix_index];
742  int32 num_accesses = accesses.accesses.size();
743  for (int32 a = 0; a < num_accesses; a++) {
744  const Access &access = accesses.accesses[a];
745  int32 command_index = access.command_index;
746  const NnetComputation::Command &command =
747  computation_.commands[command_index];
748  if (command.command_type == kDecompressMatrix) {
749  // check that the previous access to this matrix was a compression
750  // command.
751  KALDI_ASSERT(
752  a > 0 && computation_.commands[
753  accesses.accesses[a-1].command_index].command_type ==
755  }
756  if (command.command_type == kCompressMatrix) {
757  // check that the next access to this matrix is an uncompression
758  // command.
759  int32 next_command_index = accesses.accesses[a+1].command_index;
760  KALDI_ASSERT(computation_.commands[next_command_index].command_type ==
762  command_index < middle_command &&
763  next_command_index > middle_command);
764  if (command.alpha == 0.0) {
765  // alpha == 0.0 means we're only retaining the sign; we should
766  // only do this if this is the output of a ReLU.
767  // make sure there are only 2 commands after this: the uncompress
768  // command, and a relu backprop command. (Any deallocation
769  // command doesn't show up in the list of 'accesses').
770  KALDI_ASSERT(a > 0 && command.arg2 == kCompressedMatrixUint8 &&
771  num_accesses == a + 3);
772  // make sure the next access to that matrix, apart from the
773  // uncompression command, is a ReLU propagation.
774  int32 next_command_index = accesses.accesses[a+2].command_index;
775  const NnetComputation::Command &next_command =
776  computation_.commands[next_command_index];
777  KALDI_ASSERT(next_command.command_type == kBackprop &&
778  nnet_.GetComponent(next_command.arg1)->Type() ==
779  "RectifiedLinearComponent");
780  }
781  }
782  }
783  }
784 }
785 
792  int32 num_commands = computation_.commands.size(),
793  num_submatrices = computation_.submatrices.size();
794  const std::vector<NnetComputation::SubMatrixInfo> &submatrices =
796 
797  // This maps from the memo-index > 0 to the Propagate command
798  // which created it. When the corresponding Backprop command
799  // is encountered, we delete the map element.
800  std::unordered_map<int32, int32> memo_to_command;
801 
802  for (int32 command_index = 0; command_index < num_commands; command_index++) {
803  const NnetComputation::Command &c = computation_.commands[command_index];
804  switch (c.command_type) {
805  case kAllocMatrix:
806  case kDeallocMatrix:
807  if (c.arg1 < 1 || c.arg1 >= num_submatrices ||
809  KALDI_ERR << "submatrix index out of range or invalid";
810  break;
811  case kSetConst:
812  if (c.arg1 < 1 || c.arg1 >= num_submatrices)
813  KALDI_ERR << "submatrix index out of range or invalid";
814  break;
815  case kSwapMatrix:
816  if (c.arg1 < 1 || c.arg1 >= num_submatrices ||
818  c.arg2 < 1 || c.arg2 >= num_submatrices ||
820  KALDI_ERR << "submatrix index out of range or invalid";
821  if (computation_.submatrices[c.arg1].num_rows !=
822  computation_.submatrices[c.arg2].num_rows ||
823  computation_.submatrices[c.arg1].num_cols !=
824  computation_.submatrices[c.arg2].num_cols)
825  KALDI_ERR << "Dimension mismatch in kSwapMatrix command";
826  break;
827  case kPropagate: {
828  if (c.arg1 < 0 || c.arg1 >= nnet_.NumComponents())
829  KALDI_ERR << "Component index out of range";
830  const Component *component = nnet_.GetComponent(c.arg1);
831  int32 properties = component->Properties();
832  if (c.arg2 < 0 ||
834  KALDI_ERR << "Precomputed-indexes index out of range";
835  if (c.arg2 != 0 && (properties & kSimpleComponent))
836  KALDI_ERR << "Precomputed-indexes index nonzero for simple component";
837  // note: input may be the empty matrix (in unusual circumstances, for non-simple
838  // components).
839  if (c.arg3 < 0 || c.arg3 >= num_submatrices ||
840  (c.arg3 == 0 && (properties & kSimpleComponent)) ||
841  c.arg4 < 1 || c.arg4 >= num_submatrices)
842  KALDI_ERR << "Sub-matrix indexes out of range.";
843  if (c.arg3 > 0 && submatrices[c.arg3].num_cols != component->InputDim())
844  KALDI_ERR << "Input-dim mismatch.";
845  if (submatrices[c.arg4].num_cols != component->OutputDim())
846  KALDI_ERR << "Input-dim mismatch.";
847  if ((properties & kSimpleComponent) &&
848  submatrices[c.arg3].num_rows !=
849  submatrices[c.arg4].num_rows)
850  KALDI_ERR << "Num-rows mismatch for simple component.";
851  if (!(properties & kPropagateInPlace) &&
852  c.arg3 == c.arg4)
853  KALDI_ERR << "In-place propagation not supported for this component";
854  if (c.arg5 > 0) {
855  KALDI_ASSERT(memo_to_command.count(c.arg5) == 0 &&
856  "Memo index re-used.");
857  memo_to_command[c.arg5] = command_index;
858  }
859  KALDI_ASSERT(c.arg6 == 0 || c.arg6 == 1);
860  break;
861  }
862  case kBackprop:
863  case kBackpropNoModelUpdate: {
864  if (c.arg1 < 0 || c.arg1 >= nnet_.NumComponents())
865  KALDI_ERR << "Component index in backprop invalid or out of range";
866  const Component *component = nnet_.GetComponent(c.arg1);
867  int32 properties = component->Properties();
868  if (c.arg2 < 0 ||
870  KALDI_ERR << "Precomputed-indexes index out of range";
871  if (c.arg2 != 0 && (properties & kSimpleComponent))
872  KALDI_ERR << "Precomputed-indexes index nonzero for simple component";
873  // output-deriv (arg5) must be supplied; others could plausibly be zero.
874  if (c.arg3 < 0 || c.arg3 >= num_submatrices ||
875  c.arg4 < 0 || c.arg4 >= num_submatrices ||
876  c.arg5 < 1 || c.arg5 >= num_submatrices ||
877  c.arg6 < 0 || c.arg6 >= num_submatrices)
878  KALDI_ERR << "Submatrix index out of range for backprop.";
879  if ((properties & kBackpropNeedsInput) && c.arg3 == 0)
880  KALDI_ERR << "Backprop input needed but not supplied.";
881  if ((properties & kBackpropNeedsOutput) && c.arg4 == 0)
882  KALDI_ERR << "Backprop output needed but not supplied.";
883  if (c.arg6 == 0 && !(properties & kUpdatableComponent)) {
884  // note: we could perhaps make this just a warning,
885  // or optimize it away somehow.
886  KALDI_ERR << "Backprop is done but has no effect.";
887  }
888  if (c.arg5 == c.arg6 && !(properties & kBackpropInPlace))
889  KALDI_ERR << "In-place backprop used where not supported.";
890  if (c.arg3 != 0 &&
891  submatrices[c.arg3].num_cols != component->InputDim())
892  KALDI_ERR << "Input-dim mismatch in backprop.";
893  if (c.arg4 != 0 &&
894  submatrices[c.arg4].num_cols != component->OutputDim())
895  KALDI_ERR << "Output-dim mismatch in backprop.";
896  if (c.arg5 != 0 &&
897  submatrices[c.arg5].num_cols != component->OutputDim())
898  KALDI_ERR << "Output-dim mismatch in backprop.";
899  if (c.arg6 != 0 &&
900  submatrices[c.arg6].num_cols != component->InputDim())
901  KALDI_ERR << "Input-dim mismatch in backprop.";
902  // check num-rows consistency for input.
903  if (c.arg3 != 0 && c.arg6 != 0 &&
904  submatrices[c.arg3].num_rows != submatrices[c.arg6].num_rows)
905  KALDI_ERR << "Num-rows mismatch in backprop input";
906  // check num-rows consistency for output
907  if (c.arg4 != 0 &&
908  submatrices[c.arg4].num_rows != submatrices[c.arg5].num_rows)
909  KALDI_ERR << "Num-rows mismatch in backprop output";
910  if ((properties & kSimpleComponent) && c.arg6 != 0 &&
911  submatrices[c.arg5].num_rows != submatrices[c.arg6].num_rows)
912  KALDI_ERR << "Num-rows mismatch in backprop input vs output.";
913  if (c.arg7 != 0) {
914  KALDI_ASSERT(c.arg7 > 0);
915  if (memo_to_command.count(c.arg7) == 0)
916  KALDI_ERR << "Memo-index " << c.arg7 << " not used for propagate.";
917  int32 propagate_command = memo_to_command[c.arg7];
918  memo_to_command.erase(c.arg7);
919  if (c.arg1 != computation_.commands[propagate_command].arg1)
920  KALDI_ERR << "Mismatch in component-node for memo index";
921  if (!(properties & kUsesMemo))
922  KALDI_ERR << "Component not expected to use a memo.";
923  }
924  break;
925  }
926  case kMatrixCopy:
927  case kMatrixAdd:
928  if (c.arg1 < 1 || c.arg1 >= num_submatrices ||
929  c.arg2 < 1 || c.arg2 >= num_submatrices)
930  KALDI_ERR << "Submatrix indexes out of range in matrix copy/add";
931  if (submatrices[c.arg1].num_rows != submatrices[c.arg2].num_rows ||
932  submatrices[c.arg1].num_cols != submatrices[c.arg2].num_cols)
933  KALDI_ERR << "Submatrix indexes out of range in matrix copy/add";
934  if (c.arg1 == c.arg2) {
935  // we allow copying to itself if alpha != 1.0; this is how we
936  // implement scaling.
937  if (!(c.command_type == kMatrixCopy && c.alpha != 1.0)) {
938  KALDI_ERR << "Adding/copying to self";
939  }
940  }
941  break;
942  case kAddRows:
943  case kCopyRows: {
944  if (c.arg1 < 1 || c.arg1 >= num_submatrices ||
945  c.arg2 < 1 || c.arg2 >= num_submatrices ||
946  static_cast<size_t>(c.arg3) >= computation_.indexes.size())
947  KALDI_ERR << "Index out of range in add-rows/copy-rows command.";
948  const std::vector<int32> &indexes = computation_.indexes[c.arg3];
949  if (indexes.size() != static_cast<size_t>(submatrices[c.arg1].num_rows))
950  KALDI_ERR << "Indexes size mismatch in add-rows/copy-rows";
951  if (submatrices[c.arg1].num_cols != submatrices[c.arg2].num_cols)
952  KALDI_ERR << "Dimension mismatch in add-rows/copy-rows";
953  if (*std::max_element(indexes.begin(), indexes.end()) >=
954  submatrices[c.arg2].num_rows)
955  KALDI_ERR << "Row-index out of range in add-rows/copy-rows";
956  if (c.arg1 == c.arg2)
957  KALDI_ERR << "Copying to self in add-rows/copy-rows command.";
958  break;
959  }
960  case kAddRowsMulti:
961  case kCopyRowsMulti:
962  case kAddToRowsMulti:
963  case kCopyToRowsMulti: {
964  if (c.arg1 < 1 || c.arg1 >= num_submatrices ||
965  static_cast<size_t>(c.arg2) >= computation_.indexes_multi.size())
966  KALDI_ERR << "Index out of range in *-multi command";
967  const std::vector<std::pair<int32, int32> > pairs =
969  int32 num_rows = submatrices[c.arg1].num_rows,
970  num_cols = submatrices[c.arg1].num_cols;
971  if (pairs.size() != static_cast<size_t>(num_rows))
972  KALDI_ERR << "Indexes dimension mismatch in *-multi command";
973  std::vector<std::pair<int32, int32> >::const_iterator
974  iter = pairs.begin(), end = pairs.end();
975  for (; iter != end; ++iter) {
976  int32 submatrix_index = iter->first, row_index = iter->second;
977  if (submatrix_index == -1) {
978  if (row_index != -1)
979  KALDI_ERR << "Expected -1 row index if submatrix index is -1";
980  } else {
981  if (submatrix_index < 1 || submatrix_index >= num_submatrices)
982  KALDI_ERR << "Submatrix index out of range in indexes_multi";
983  if (row_index < 0 ||
984  row_index >= submatrices[submatrix_index].num_rows)
985  KALDI_ERR << "Row index out of range in indexes_multi";
986  if (submatrix_index == c.arg1)
987  KALDI_ERR << "Copying from self in *-multi command.";
988  if (submatrices[submatrix_index].num_cols != num_cols)
989  KALDI_ERR << "Mismatching dimension in *-multi command";
990  }
991  }
992  if (c.command_type == kAddToRowsMulti ||
994  // check for duplicates; these are not allowed in kAddToRowsMulti
995  // or kCopyToRowsMulti because they would necessitate extra work
996  // in CUDA kernels.
997  std::vector<std::pair<int32, int32> > pairs_copy(pairs);
998  std::sort(pairs_copy.begin(), pairs_copy.end());
999  std::vector<std::pair<int32, int32> >::const_iterator
1000  iter = pairs_copy.begin(), end = pairs_copy.end(),
1001  next_iter;
1002  for (; iter != end; ++iter) {
1003  next_iter = iter;
1004  ++next_iter;
1005  if (next_iter != end && *iter == *next_iter &&
1006  iter->first != -1) {
1007  KALDI_ERR << "Duplicate element "
1008  << iter->first << ',' << iter->second << " found in "
1009  << "indexes for {add,copy}-to-rows-multi command.";
1010  }
1011  }
1012  }
1013  break;
1014  }
1015  case kAddRowRanges: {
1016  if (c.arg1 < 1 || c.arg1 >= num_submatrices ||
1017  c.arg2 < 1 || c.arg2 >= num_submatrices ||
1018  static_cast<size_t>(c.arg3) >= computation_.indexes_ranges.size())
1019  KALDI_ERR << "Index out of range in add-row-ranges command";
1020  const std::vector<std::pair<int32, int32> > pairs =
1022  if (static_cast<size_t>(submatrices[c.arg1].num_rows) != pairs.size())
1023  KALDI_ERR << "Num-rows mismatch in add-row-ranges command";
1024  if (submatrices[c.arg1].num_cols != submatrices[c.arg2].num_cols)
1025  KALDI_ERR << "Dimension mismatch in add-row-ranges command";
1026  int32 src_num_rows = submatrices[c.arg2].num_rows;
1027  std::vector<std::pair<int32, int32> >::const_iterator
1028  iter = pairs.begin(), end = pairs.end();
1029  for (; iter != end; ++iter) {
1030  if (!((iter->first == -1 && iter->second == -1) ||
1031  (iter->second > iter->first &&
1032  iter->first >= 0 && iter->second <= src_num_rows)))
1033  KALDI_ERR << "Row range " << iter->first << ',' << iter->second
1034  << " is invalid in add-row-ranges command.";
1035  }
1036  break;
1037  }
1038  case kCompressMatrix: {
1039  if (c.arg1 < 1 || c.arg1 >= num_submatrices ||
1041  KALDI_ERR << "submatrix index out of range or invalid";
1042  if (c.arg2 < static_cast<int32>(kCompressedMatrixInt8) ||
1043  c.arg2 > static_cast<int32>(kCompressedMatrixUint16))
1044  KALDI_ERR << "Invalid compressed-matrix type.";
1045  if (c.arg3 != 0 && c.arg3 != 1)
1046  KALDI_ERR << "Invalid 'truncate' option for compressing matrix.";
1047  if (c.alpha < 0.0 || c.alpha > 1000.0 ||
1048  (c.alpha == 0.0 && c.arg2 != kCompressedMatrixUint8))
1049  KALDI_ERR << "Invalid alpha in kCompressMatrix command.";
1050  break;
1051  }
1052  case kDecompressMatrix: {
1053  if (c.arg1 < 1 || c.arg1 >= num_submatrices ||
1055  KALDI_ERR << "submatrix index out of range or invalid";
1056  break;
1057  }
1058  case kAcceptInput: case kProvideOutput: {
1059  if (c.arg1 < 1 || c.arg1 >= num_submatrices ||
1061  KALDI_ERR << "submatrix index out of range or invalid";
1062  // note: we may later change the following condition to allow component
1063  // nodes. we allow it on output node because of derivatives.
1064  if (!nnet_.IsInputNode(c.arg2) && !nnet_.IsOutputNode(c.arg2))
1065  KALDI_ERR << "Invalid network node";
1066  break;
1067  }
1068  case kNoOperation:
1069  case kNoOperationPermanent:
1070  case kNoOperationMarker:
1071  case kNoOperationLabel:
1072  break;
1073  case kGotoLabel: {
1074  int32 label_index = c.arg1;
1075  if (label_index < 0 || label_index >= command_index ||
1076  computation_.commands[label_index].command_type != kNoOperationLabel)
1077  KALDI_ERR << "kGotoLabel command has invalid destination index.";
1078  if (command_index + 1 != num_commands) {
1079  KALDI_ERR << "kGotoLabel is not the last command in the computation";
1080  }
1081  break;
1082  }
1083  default:
1084  KALDI_ERR << "Unknown command type.";
1085  }
1086  }
1087  if (!memo_to_command.empty()) {
1088  KALDI_ERR << "Memo was used in command "
1089  << memo_to_command.begin()->second
1090  << " but never consumed.";
1091  }
1092 }
1093 
1095  if (computation_.matrix_debug_info.empty()) return;
1096  if (computation_.matrix_debug_info.size() !=
1097  computation_.matrices.size())
1098  KALDI_ERR << "Debug info has wrong size";
1099  for (size_t i = 1; i < computation_.matrix_debug_info.size(); i++) {
1100  if (computation_.matrix_debug_info[i].cindexes.size() !=
1101  static_cast<size_t>(computation_.matrices[i].num_rows))
1102  KALDI_ERR << "Debug info for matrix m" << i
1103  << " has wrong num-rows.";
1104  std::vector<Cindex>::const_iterator
1105  iter = computation_.matrix_debug_info[i].cindexes.begin(),
1106  end = computation_.matrix_debug_info[i].cindexes.end();
1107  for (; iter != end; ++iter) {
1108  if (iter->second.n < 0) {
1109  KALDI_ERR << "Negative n index in debug info";
1110  }
1111  }
1112  }
1113 }
1114 
1115 
1116 // note: 'computation' is not a reference, it's copied so that we
1117 // can modify it internally.
1118 static void CheckComputationOnline(const Nnet &nnet,
1119  NnetComputation computation,
1120  bool check_rewrite) {
1121  int32 num_commands = computation.commands.size();
1122  KALDI_ASSERT(computation.commands[num_commands-1].command_type == kGotoLabel);
1123  for (int32 c = num_commands - 2;
1124  c >= 0 && computation.commands[c].command_type == kSwapMatrix;
1125  c--) {
1126  // this command can be interpreted as "initialize matrix referred to by
1127  // c.arg2 with the matrix referred to by c.arg2".
1128  // Because this would be interpreted by the analysis code as initializing a
1129  // matrix that has already been initialized, we turn this into a command
1130  // that just deallocates the matrix in c.arg2. [note: all these indexes
1131  // are actually submatrix indexes].
1132  computation.commands[c].command_type = kDeallocMatrix;
1133  std::swap(computation.commands[c].arg1, computation.commands[c].arg2);
1134  }
1135 
1137  opts.check_rewrite = check_rewrite;
1138  opts.check_unused_variables = false;
1139  // We can always do this check with online computations, since they do not
1140  // have the RemoveUnnecessaryAllocation() optimization applied.
1141  ComputationChecker checker(opts, nnet, computation);
1142  checker.Check();
1143 }
1144 
1145 void CheckComputation(const Nnet &nnet,
1146  const NnetComputation &computation,
1147  bool check_rewrite) {
1148  try {
1149  if (!computation.commands.empty() &&
1150  computation.commands.back().command_type == kGotoLabel) {
1151  // Online computations need to be treated specially.
1152  CheckComputationOnline(nnet, computation, check_rewrite);
1153  } else {
1155  opts.check_rewrite = check_rewrite;
1156  ComputationChecker checker(opts, nnet, computation);
1157  checker.Check();
1158  }
1159  } catch (...) {
1160  computation.Print(std::cerr, nnet);
1161  KALDI_ERR << "Computation check failed for computation printed above "
1162  "(actual error message is above computation)";
1163  }
1164 }
1165 
1167  const NnetComputation &computation,
1168  std::vector<std::vector<int32> > *mat_to_submat) {
1169  int32 num_matrices = computation.matrices.size(),
1170  num_submatrices = computation.submatrices.size();
1171  mat_to_submat->clear();
1172  mat_to_submat->resize(num_matrices);
1173  for (int32 submatrix_index = 1;
1174  submatrix_index < num_submatrices;
1175  submatrix_index++) {
1176  int32 matrix_index = computation.submatrices[submatrix_index].matrix_index;
1177  KALDI_ASSERT(matrix_index > 0 && matrix_index < num_matrices);
1178  (*mat_to_submat)[matrix_index].push_back(submatrix_index);
1179  }
1180 }
1181 
1183  KALDI_ASSERT(static_cast<size_t>(s) < computation_.submatrices.size() && s>0);
1184  int32 ans = computation_.commands.size();
1185  std::vector<int32> variable_indexes;
1186  analyzer_.variables.AppendVariablesForSubmatrix(s, &variable_indexes);
1187  std::vector<int32>::const_iterator iter = variable_indexes.begin(),
1188  end = variable_indexes.end();
1189  for (; iter != end; ++iter) {
1190  int32 v = *iter;
1191  const std::vector<Access> &accesses = analyzer_.variable_accesses[v];
1192  std::vector<Access>::const_iterator access_iter = accesses.begin(),
1193  access_end = accesses.end();
1194  for (; access_iter != access_end; ++access_iter) {
1195  int32 command_index = access_iter->command_index;
1197  command_index];
1198  if (!(command.command_type == kSetConst &&
1199  command.alpha == 0.0)) { // if it's not a zeroing command..
1200  ans = std::min(ans, command_index);
1201  break; // break from access_iter loop (an optimization)
1202  }
1203  }
1204  }
1205  return ans;
1206 }
1207 
1208 
1210  KALDI_ASSERT(static_cast<size_t>(s) < computation_.submatrices.size() && s>0);
1211  int32 ans = computation_.commands.size();
1212  std::vector<int32> variable_indexes;
1213  analyzer_.variables.AppendVariablesForSubmatrix(s, &variable_indexes);
1214  std::vector<int32>::const_iterator iter = variable_indexes.begin(),
1215  end = variable_indexes.end();
1216  for (; iter != end; ++iter) {
1217  int32 v = *iter;
1218  const std::vector<Access> &accesses = analyzer_.variable_accesses[v];
1219  if (!accesses.empty())
1220  ans = std::min(ans, accesses[0].command_index);
1221  }
1222  return ans;
1223 }
1224 
1225 
1227  KALDI_ASSERT(static_cast<size_t>(m) < computation_.matrices.size() && m > 0);
1228  int32 ans = computation_.commands.size();
1229  const std::vector<Access> &accesses =
1230  analyzer_.matrix_accesses[m].accesses;
1231  std::vector<Access>::const_iterator access_iter = accesses.begin(),
1232  access_end = accesses.end();
1233  for (; access_iter != access_end; ++access_iter) {
1234  int32 command_index = access_iter->command_index;
1235  const NnetComputation::Command command =
1236  computation_.commands[command_index];
1237  if (!(command.command_type == kSetConst &&
1238  command.alpha == 0.0)) { // except for zeroing commands..
1239  ans = std::min(ans, command_index);
1240  break; // break from access_iter loop (an optimization; note, the
1241  // list 'accesses' is sorted.)
1242  }
1243  }
1244  return ans;
1245 }
1246 
1247 
1249  KALDI_ASSERT(static_cast<size_t>(m) < computation_.matrices.size() && m > 0);
1250  int32 ans = -1;
1251  const std::vector<Access> &accesses =
1252  analyzer_.matrix_accesses[m].accesses;
1253  std::vector<Access>::const_reverse_iterator access_iter = accesses.rbegin(),
1254  access_end = accesses.rend();
1255  for (; access_iter != access_end; ++access_iter) {
1256  int32 command_index = access_iter->command_index;
1257  ans = std::max(ans, command_index);
1258  break; // break from access_iter loop (an optimization)
1259  }
1260  return ans;
1261 }
1262 
1263 
1265  KALDI_ASSERT(static_cast<size_t>(s) < computation_.submatrices.size() && s>0);
1266  int32 ans = -1;
1267  std::vector<int32> variable_indexes;
1268  analyzer_.variables.AppendVariablesForSubmatrix(s, &variable_indexes);
1269  std::vector<int32>::const_iterator iter = variable_indexes.begin(),
1270  end = variable_indexes.end();
1271  for (; iter != end; ++iter) {
1272  int32 v = *iter;
1273  const std::vector<Access> &accesses = analyzer_.variable_accesses[v];
1274  // Go through the variable accesses in reverse order (of command index)
1275  std::vector<Access>::const_reverse_iterator access_iter = accesses.rbegin(),
1276  access_end = accesses.rend();
1277  for (; access_iter != access_end; ++access_iter) {
1278  int32 command_index = access_iter->command_index;
1279  CommandType command_type =
1280  computation_.commands[command_index].command_type;
1281  // deallocation command should not be listed here.
1282  KALDI_ASSERT(command_type != kDeallocMatrix);
1283  ans = std::max(ans, command_index);
1284  break; // break from access_iter loop (an optimization)
1285  }
1286  }
1287  return ans;
1288 }
1289 
1290 
1292  KALDI_ASSERT(static_cast<size_t>(s) < computation_.submatrices.size() && s>0);
1293  int32 matrix_index = computation_.submatrices[s].matrix_index;
1294  if (analyzer_.matrix_accesses[matrix_index].is_output)
1295  return computation_.commands.size();
1296  int32 ans = -1;
1297  std::vector<int32> variable_indexes;
1298  analyzer_.variables.AppendVariablesForSubmatrix(s, &variable_indexes);
1299  std::vector<int32>::const_iterator iter = variable_indexes.begin(),
1300  end = variable_indexes.end();
1301  for (; iter != end; ++iter) {
1302  int32 v = *iter;
1303  const std::vector<Access> &accesses = analyzer_.variable_accesses[v];
1304  // Go through the variable accesses in reverse order (of command index)
1305  std::vector<Access>::const_reverse_iterator access_iter = accesses.rbegin(),
1306  access_end = accesses.rend();
1307  for (; access_iter != access_end; ++access_iter) {
1308  int32 command_index = access_iter->command_index;
1309  CommandType command_type =
1310  computation_.commands[command_index].command_type;
1311  // deallocation command should not be listed here.
1312  KALDI_ASSERT(command_type != kDeallocMatrix);
1313  if (access_iter->access_type != kReadAccess) {
1314  // If this operation is of type kWriteAccess or kReadWriteAccess
1315  ans = std::max(ans, command_index);
1316  break; // break from access_iter loop (an optimization)
1317  }
1318  }
1319  }
1320  return ans;
1321 }
1322 
1324  KALDI_ASSERT(static_cast<size_t>(c) < computation_.commands.size());
1325  KALDI_ASSERT(static_cast<size_t>(s) < computation_.submatrices.size() && s>0);
1326  int32 matrix_index = computation_.submatrices[s].matrix_index;
1327  int32 ans = analyzer_.matrix_accesses[matrix_index].deallocate_command;
1328  if (ans == -1)
1329  ans = static_cast<int32>(computation_.commands.size());
1330  std::vector<int32> variable_indexes;
1331  analyzer_.variables.AppendVariablesForSubmatrix(s, &variable_indexes);
1332  std::vector<int32>::const_iterator iter = variable_indexes.begin(),
1333  end = variable_indexes.end();
1334  for (; iter != end; ++iter) {
1335  int32 v = *iter;
1336  const std::vector<Access> &accesses = analyzer_.variable_accesses[v];
1337  std::vector<Access>::const_iterator access_iter = accesses.begin(),
1338  access_end = accesses.end();
1339  for (; access_iter != access_end; ++access_iter) {
1340  int32 command_index = access_iter->command_index;
1341  if (command_index > c &&
1342  access_iter->access_type != kReadAccess) {
1343  ans = std::min(ans, command_index);
1344  }
1345  }
1346  }
1347  return ans;
1348 }
1349 
1350 void PrintMatrixAccesses(std::ostream &os,
1351  const std::vector<MatrixAccesses> &matrix_accesses) {
1352  int32 num_matrices = matrix_accesses.size();
1353  for (int32 m = 1; m < num_matrices; m++) {
1354  const MatrixAccesses &a = matrix_accesses[m];
1355  os << "m" << m << ": init-command=" << a.allocate_command
1356  << ", destroy-command=" << a.deallocate_command
1357  << ", accesses=";
1358  std::vector<Access>::const_iterator iter = a.accesses.begin(),
1359  end = a.accesses.end();
1360  for (; iter != end; ++iter)
1361  os << 'c' << iter->command_index << "("
1362  << (iter->access_type == kReadAccess ? "r" :
1363  (iter->access_type == kWriteAccess ? "w" : "rw")) << ") ";
1364  os << "\n";
1365  }
1366 }
1367 
1368 void PrintCommandAttributes(std::ostream &os,
1369  const std::vector<CommandAttributes> &attributes) {
1370  int32 num_commands = attributes.size();
1371  for (int32 c = 0; c < num_commands; c++) {
1372  const CommandAttributes &this_attr = attributes[c];
1373  os << "c" << c << ": ";
1374  if (!this_attr.variables_read.empty()) {
1375  os << "r(";
1376  std::vector<int32>::const_iterator iter = this_attr.variables_read.begin(),
1377  end = this_attr.variables_read.end();
1378  for (; iter != end; ++iter) {
1379  os << "v" << *iter;
1380  if (iter+1 != end) os << ",";
1381  }
1382  os << ") ";
1383  }
1384  if (!this_attr.variables_written.empty()) {
1385  os << "w(";
1386  std::vector<int32>::const_iterator
1387  iter = this_attr.variables_written.begin(),
1388  end = this_attr.variables_written.end();
1389  for (; iter != end; ++iter) {
1390  os << "v" << *iter;
1391  if (iter+1 != end) os << ",";
1392  }
1393  os << ") ";
1394  }
1395  if (!this_attr.matrices_read.empty()) {
1396  os << "r(";
1397  std::vector<int32>::const_iterator iter = this_attr.matrices_read.begin(),
1398  end = this_attr.matrices_read.end();
1399  for (; iter != end; ++iter) {
1400  os << "m" << *iter;
1401  if (iter+1 != end) os << ",";
1402  }
1403  os << ") ";
1404  }
1405  if (!this_attr.matrices_written.empty()) {
1406  os << "w(";
1407  std::vector<int32>::const_iterator
1408  iter = this_attr.matrices_written.begin(),
1409  end = this_attr.matrices_written.end();
1410  for (; iter != end; ++iter) {
1411  os << "m" << *iter;
1412  if (iter+1 != end) os << ",";
1413  }
1414  os << ")";
1415  }
1416  os << "\n";
1417  }
1418 }
1419 
1420 
1421 void Analyzer::Init(const Nnet &nnet, const NnetComputation &computation) {
1422  variables.Init(computation);
1423  ComputeCommandAttributes(nnet, computation, variables, &command_attributes);
1424  ComputeVariableAccesses(variables, command_attributes, &variable_accesses);
1425  ComputeMatrixAccesses(nnet, computation, variables, command_attributes,
1426  &matrix_accesses);
1427 }
1428 
1429 void GetCommandsOfType(const NnetComputation &computation,
1430  CommandType t,
1431  std::vector<int32> *command_indexes) {
1432  int32 num_commands = computation.commands.size();
1433  command_indexes->clear();
1434  for (int32 c = 0; c < num_commands; c++)
1435  if (computation.commands[c].command_type == t)
1436  command_indexes->push_back(c);
1437 }
1438 
1439 int64 GetMaxMemoryUse(const NnetComputation &computation) {
1440  int64 cur_memory_use = 0,
1441  max_memory_use = 0;
1442  int32 num_commands = computation.commands.size(),
1443  num_submatrices = computation.submatrices.size();
1444  // the vector 'num_compressed_bytes' is used to remember the number of bytes
1445  // in the compressed matrices for each submatrix (this will only be used for
1446  // those that correspond to a 'whole matrix). It's needed because the
1447  // decompression command doesn't tell us what compression type was used for
1448  // that matrix.
1449  std::vector<int32> num_compressed_bytes(num_submatrices, -100000000);
1450  for (int32 command_index = 0; command_index < num_commands; ++command_index) {
1451  const NnetComputation::Command &c = computation.commands[command_index];
1452  int64 this_num_bytes = -100000000,
1453  this_compressed_num_bytes = -10000000;
1454 
1455 
1456  if (c.arg1 >= 0 && c.arg1 < num_submatrices) {
1457  // if arg1 could plausibly be a sub-matrix index...
1458  const NnetComputation::SubMatrixInfo &submat_info =
1459  computation.submatrices[c.arg1];
1460  this_num_bytes = static_cast<int64>(sizeof(BaseFloat)) *
1461  submat_info.num_rows * submat_info.num_cols;
1462 
1463  if (c.command_type == kCompressMatrix) {
1464  this_compressed_num_bytes =
1465  ((c.arg2 == static_cast<int32>(kCompressedMatrixInt8) ||
1466  c.arg2 == static_cast<int32>(kCompressedMatrixUint8)) ?
1467  1 : 2) * static_cast<int64>(submat_info.num_rows) *
1468  submat_info.num_cols;
1469  num_compressed_bytes[c.arg1] = this_compressed_num_bytes;
1470  } else if (c.command_type == kDecompressMatrix) {
1471  this_compressed_num_bytes = num_compressed_bytes[c.arg1];
1472  }
1473  }
1474  switch (c.command_type) {
1475  case kAllocMatrix:
1476  case kAcceptInput:
1477  cur_memory_use += this_num_bytes;
1478  break;
1479  case kDeallocMatrix:
1480  cur_memory_use -= this_num_bytes;
1481  break;
1482  case kCompressMatrix:
1483  cur_memory_use += this_compressed_num_bytes - this_num_bytes;
1484  break;
1485  case kDecompressMatrix:
1486  cur_memory_use += this_num_bytes - this_compressed_num_bytes;
1487  break;
1488  default:
1489  break;
1490  }
1491  KALDI_ASSERT(cur_memory_use >= 0);
1492  if (cur_memory_use > max_memory_use)
1493  max_memory_use = cur_memory_use;
1494  }
1495  return max_memory_use;
1496 }
1497 
1498 } // namespace nnet3
1499 } // namespace kaldi
void Init(const NnetComputation &computation)
CommandType
CommandType is an enum that describes the category of the command used in the NnetComputation.
This code computes Goodness of Pronunciation (GOP) and extracts phone-level pronunciation feature for...
Definition: chain.dox:20
void AppendVariablesForMatrix(int32 matrix_index, std::vector< int32 > *variable_indexes) const
Appends to variables_indexes the sorted list of variables corresponding to a matrix index...
int32 FirstNontrivialAccess(int32 s) const
Returns the first command (read or write) that accesses any part of &#39;s&#39; except for zeroing it (i...
void GetCommandsOfType(const NnetComputation &computation, CommandType t, std::vector< int32 > *command_indexes)
This utility function works out from a computation, the command-indexes of the commands of the given ...
ComputationChecker(const CheckComputationOptions &config, const Nnet &nnet, const NnetComputation &computation)
std::vector< MatrixDebugInfo > matrix_debug_info
void PrintMatrixAccesses(std::ostream &os, const std::vector< MatrixAccesses > &matrix_accesses)
This function is to be used in debugging; it produces human-readable output.
Abstract base-class for neural-net components.
void PrintCommandAttributes(std::ostream &os, const std::vector< CommandAttributes > &attributes)
This function is to be used in debugging; it produces human-readable output.
void Print(std::ostream &os, const Nnet &nnet) const
std::vector< int32 > submatrix_to_matrix_
Definition: nnet-analyze.h:199
This file contains utilities for analyzing and checking computations, which are used in the optimizat...
bool IsInputNode(int32 node) const
Returns true if this is an output node, meaning that it is of type kInput.
Definition: nnet-nnet.cc:120
void swap(basic_filebuf< CharT, Traits > &x, basic_filebuf< CharT, Traits > &y)
kaldi::int32 int32
std::vector< MatrixInfo > matrices
virtual int32 OutputDim() const =0
Returns output-dimension of this component.
int32 GetMatrixForVariable(int32 variable) const
void SortAndUniq(std::vector< T > *vec)
Sorts and uniq&#39;s (removes duplicates) from a vector.
Definition: stl-utils.h:39
void ComputeCommandAttributes(const Nnet &nnet, const NnetComputation &computation, const ComputationVariables &vars, std::vector< CommandAttributes > *attributes)
std::vector< Command > commands
static void IndexesMultiToSubmatrixIndexes(const std::vector< std::pair< int32, int32 > > &indexes_multi, std::vector< int32 > *submatrix_indexes)
given a vector of pairs from computation.indexes_multi_indexes containing paris (submatrix-index, row-index), this function outputs to "submatrix_indexes" all (unique) submatrix indexes that appear; and it outputs to "contains_null_marker" true if the pair (-1, -1) appears anywhere in indexes_multi, and false otherwise.
int32 LastMatrixAccess(int32 m) const
Returns the last non-deallocation command that accesses any part of matrix &#39;m&#39;; if there is no such c...
const NnetComputation & computation_
Definition: nnet-analyze.h:433
std::vector< int32 > submatrices_written
Definition: nnet-analyze.h:56
std::vector< Access > accesses
Records the indexes of commands that access the matrix, and the type (read, read/write, write).
Definition: nnet-analyze.h:264
std::vector< int32 > matrices_read
Definition: nnet-analyze.h:59
std::vector< std::vector< int32 > > row_split_points_
Definition: nnet-analyze.h:188
const size_t count
std::vector< int32 > variables_written
Definition: nnet-analyze.h:51
std::vector< std::vector< int32 > > variables_for_submatrix_
Definition: nnet-analyze.h:213
float BaseFloat
Definition: kaldi-types.h:29
int64 GetMaxMemoryUse(const NnetComputation &computation)
This class relates the matrices and sub-matrices in the computation to imaginary "variables", such that we can think of the operations as operating on sets of individual variables, and we can then do analysis that lets us do optimization.
Definition: nnet-analyze.h:121
std::vector< int32 > variable_to_matrix_
Definition: nnet-analyze.h:207
bool IsOutputNode(int32 node) const
Returns true if this is an output node, meaning that it is of type kDescriptor and is not directly fo...
Definition: nnet-nnet.cc:112
std::vector< int32 > matrix_to_variable_index_
Definition: nnet-analyze.h:197
void Init(const Nnet &nnet, const NnetComputation &computation)
std::vector< std::vector< std::pair< int32, int32 > > > indexes_multi
void CheckComputationIndexes() const
This very basic check just makes sure that all indexes in the commands are within range...
void CheckComputationRewrite() const
Checks for the situation where a read-only operation on a variable is followed by an operation that w...
virtual int32 Properties() const =0
Return bitmask of the component&#39;s properties.
void RecordAccessForSubmatrix(int32 submatrix_index, AccessType access_type, CommandAttributes *ca) const
void ComputeVariableAccesses(const ComputationVariables &variables, const std::vector< CommandAttributes > &command_attributes, std::vector< std::vector< Access > > *variable_accesses)
After the command-level attributes have been computed, this function organizes them per variable (see...
static int32 FindIndexOf(const std::vector< int32 > &sorted_vec, int32 i)
Definition: nnet-analyze.cc:73
const CheckComputationOptions & config_
Definition: nnet-analyze.h:431
int32 FirstNontrivialMatrixAccess(int32 m) const
Returns the first command that is not a zeroing command (kSetConst with alpha=0.0), that accesses any part of &#39;m&#39; [note: allocation and deallocation do not count a matrix accesses.
std::vector< SubMatrixInfo > submatrices
#define KALDI_ERR
Definition: kaldi-error.h:147
int32 FirstAccess(int32 s) const
Returns the first command (read or write) that accesses any part of &#39;s&#39;, including possibly zeroing i...
#define KALDI_WARN
Definition: kaldi-error.h:150
std::vector< std::vector< Access > > variable_accesses
Definition: nnet-analyze.h:297
std::vector< std::vector< int32 > > column_split_points_
Definition: nnet-analyze.h:185
void CheckComputationUndefined() const
Checks for the situation where a variable is read before being written.
int32 deallocate_command
Index of the command that deallocates the matrix (which will be of type kDeallocMatrix or kSwapMatrix...
Definition: nnet-analyze.h:257
std::vector< int32 > submatrices_read
Definition: nnet-analyze.h:54
int32 LastWriteAccess(int32 s) const
Returns the last command-index that accesses any part of submatrix &#39;s&#39; as a write operation...
void ComputeSplitPoints(const NnetComputation &computation)
Definition: nnet-analyze.cc:25
int32 DataInvalidatedCommand(int32 c, int32 s) const
Returns (the first command-index after &#39;c&#39; that any part of submatrix &#39;s&#39; is written to); or if there...
std::vector< int32 > matrices_written
Definition: nnet-analyze.h:61
int32 LastAccess(int32 s) const
Returns the last non-deallocation command that accesses any part of submatrix &#39;s&#39;; if there is no suc...
Component * GetComponent(int32 c)
Return component indexed c. Not a copy; not owned by caller.
Definition: nnet-nnet.cc:150
NnetComputation::SubMatrixInfo VariableInfo(int32 variable) const
std::vector< PrecomputedIndexesInfo > component_precomputed_indexes
virtual std::string Type() const =0
Returns a string such as "SigmoidComponent", describing the type of the object.
void ComputeMatrixToSubmatrix(const NnetComputation &computation, std::vector< std::vector< int32 > > *mat_to_submat)
This function computes a vector &#39;mat_to_submat&#39;, indexed by matrix index, such that (*mat_to_submat)[...
void ComputeMatrixAccesses(const Nnet &nnet, const NnetComputation &computation, const ComputationVariables &variables, const std::vector< CommandAttributes > &command_attributes, std::vector< MatrixAccesses > *matrix_accesses)
This function organizes information in the CommandAttributes in a way that is convenient to access pe...
std::vector< MatrixAccesses > matrix_accesses
Definition: nnet-analyze.h:298
int32 NumComponents() const
Definition: nnet-nnet.h:124
void CheckComputation(const Nnet &nnet, const NnetComputation &computation, bool check_rewrite)
This is a convenience interface for class ComputationChecker.
static void CheckComputationOnline(const Nnet &nnet, NnetComputation computation, bool check_rewrite)
#define KALDI_ASSERT(cond)
Definition: kaldi-error.h:185
static bool computation_checker_warned_unused_input
Checks that we never use variables before they are allocated or after they are deallocated, and some other checks that can be done from the MatrixAccesses.
void ComputeVariablesForSubmatrix(const NnetComputation &computation)
Definition: nnet-analyze.cc:81
void AppendVariablesForSubmatrix(int32 submatrix_index, std::vector< int32 > *variable_indexes) const
int32 allocate_command
Index of the command that allocates the matrix (which will be of type kAllocMatrix or kSwapMatrix)...
Definition: nnet-analyze.h:253
bool is_input
true if this matrix is an input to the computation (i.e.
Definition: nnet-analyze.h:267
std::vector< int32 > variables_read
Definition: nnet-analyze.h:49
std::vector< std::vector< int32 > > indexes
ComputationVariables variables
Definition: nnet-analyze.h:295
std::vector< bool > submatrix_is_whole_matrix_
Definition: nnet-analyze.h:203
virtual int32 InputDim() const =0
Returns input-dimension of this component.
bool IsSortedAndUniq(const std::vector< T > &vec)
Returns true if the vector is sorted and contains each element only once.
Definition: stl-utils.h:63
bool IsWholeMatrix(int32 submatrix_index) const
std::string DescribeVariable(int32 variable) const
std::vector< std::vector< std::pair< int32, int32 > > > indexes_ranges