nnet-computation-graph.cc
Go to the documentation of this file.
1 // nnet3/nnet-computation-graph.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 <deque>
22 #include "nnet3/nnet-graph.h"
23 
24 namespace kaldi {
25 namespace nnet3 {
26 
27 
29  bool input, bool *is_new) {
30  typedef unordered_map<Cindex, int32, CindexHasher> map_type;
31  int32 new_index = cindexes.size(); // we'll add this if we don't find it.
32  std::pair<map_type::iterator, bool> p = cindex_to_cindex_id_.insert(
33  std::pair<Cindex, int32>(cindex, new_index));
34  if (p.second == true) { // We added something to the hash.
35  *is_new = true;
36  KALDI_ASSERT(is_input.size() == cindexes.size());
37  cindexes.push_back(cindex);
38  is_input.push_back(input);
39  // make room for this "dependencies" entry.
40  dependencies.resize(new_index + 1);
41  return new_index;
42  } else { // We did not add anything.
43  *is_new = false;
44  return p.first->second;
45  }
46 }
48  typedef unordered_map<Cindex, int32, CindexHasher> map_type;
49  map_type::const_iterator iter = cindex_to_cindex_id_.find(cindex);
50  if (iter == cindex_to_cindex_id_.end())
51  return -1;
52  else
53  return iter->second;
54 }
55 
56 
57 void ComputationGraph::Renumber(int32 start_cindex_id,
58  const std::vector<bool> &keep) {
59  int32 old_num_cindex_ids = cindexes.size();
60  KALDI_ASSERT(keep.size() == old_num_cindex_ids - start_cindex_id);
61  // count_before_renumbering is the number of cindex_ids >= start_cindex_id,
62  // before renumbering.
63  int32 count_before_renumbering = old_num_cindex_ids - start_cindex_id;
64  std::vector<int32> old2new(count_before_renumbering, -1), new2old;
65  new2old.reserve(old_num_cindex_ids);
66  for (int32 j = 0; j < count_before_renumbering; j++) {
67  if (keep[j]) {
68  old2new[j] = new2old.size() + start_cindex_id;
69  new2old.push_back(j + start_cindex_id);
70  }
71  }
72  // count_after_renumbering is the number of cindex_ids >= start_cindex_id,
73  // after renumbering.
74  int32 count_after_renumbering = new2old.size(),
75  new_num_cindex_ids = start_cindex_id + count_after_renumbering;
76  if (count_after_renumbering == count_before_renumbering) {
77  // this is an optimization for when we are not deleting any
78  // cindex-ids.
79  return;
80  }
81 
82  for (int32 old_cindex_id = start_cindex_id;
83  old_cindex_id < old_num_cindex_ids; old_cindex_id++) {
84  int32 new_cindex_id = old2new[old_cindex_id - start_cindex_id];
85  Cindex &cindex = cindexes[old_cindex_id];
86  if (new_cindex_id == -1) {
87  cindex_to_cindex_id_.erase(cindex);
88  } else if (new_cindex_id != old_cindex_id) {
89  cindex_to_cindex_id_[cindex] = new_cindex_id;
90  }
91  }
92 
93  std::vector<int32> temp;
94  for (int32 c = start_cindex_id; c < new_num_cindex_ids; c++) {
95  int32 d = new2old[c - start_cindex_id];
96  // note: d >= c, which is why we do not overwrite data here.
97  KALDI_PARANOID_ASSERT(d >= c);
98  cindexes[c] = cindexes[d];
99  is_input[c] = is_input[d];
100  // if c == d, we need to create a temporary copy.
101  const std::vector<int32> &src_dependencies =
102  (c == d ? (temp = dependencies[d]) : dependencies[d]);
103  std::vector<int32>::const_iterator
104  iter = src_dependencies.begin(), end = src_dependencies.end();
105  dependencies[c].clear();
106  for (; iter != end; ++iter) {
107  int32 old_dep = *iter;
108  if (old_dep < start_cindex_id) {
109  dependencies[c].push_back(old_dep);
110  } else {
111  int32 new_dep = old2new[old_dep - start_cindex_id];
112  if (new_dep != -1)
113  dependencies[c].push_back(new_dep);
114  else
115  KALDI_ERR << "Dependency on nonexistent cindex-id";
116  }
117  }
118  }
119 
120  cindexes.resize(new_num_cindex_ids);
121  is_input.resize(new_num_cindex_ids);
122  dependencies.resize(new_num_cindex_ids);
123 }
124 
126  int32 cindex_id) const {
127  KALDI_ASSERT(static_cast<size_t>(cindex_id) < graph_->cindexes.size());
128  const Cindex &cindex = graph_->cindexes[cindex_id];
129  const std::string &node_name = nnet_.GetNodeName(cindex.first);
130  os << node_name << '(' << cindex.second.n << ", " << cindex.second.t
131  << ", " << cindex.second.x << ')';
132 }
133 
135  int32 first_cindex_id) const {
136  int32 max_lines_print = 100;
137  std::deque<int32> cindexes_to_explain;
138  std::vector<bool> added_to_queue(graph_->cindexes.size(), false);
139  cindexes_to_explain.push_back(first_cindex_id);
140  added_to_queue[first_cindex_id] = true;
141  KALDI_ASSERT(graph_->cindexes.size() == graph_->dependencies.size());
142  std::ostringstream os;
143  os << "*** cindex ";
144  PrintCindexId(os, first_cindex_id);
145  os << " is not computable for the following reason: ***\n";
146  for (int32 num_lines_printed = 0;
147  num_lines_printed < max_lines_print && !cindexes_to_explain.empty();
148  num_lines_printed++) {
149  int32 cindex_id = cindexes_to_explain.front();
150  cindexes_to_explain.pop_front();
151  KALDI_ASSERT(static_cast<size_t>(cindex_id) < graph_->cindexes.size());
152  PrintCindexId(os, cindex_id);
153  os << " is " << cindex_info_[cindex_id].computable << ", dependencies: ";
154  const std::vector<int32> dependencies = graph_->dependencies[cindex_id];
155  std::vector<int32>::const_iterator iter = dependencies.begin(),
156  end = dependencies.end();
157  for (; iter != end; iter++) {
158  int32 dep_cindex_id = *iter;
159  PrintCindexId(os, dep_cindex_id);
160  const CindexInfo &dep_info = cindex_info_[dep_cindex_id];
161  os << '[' << dep_info.computable << ']';
162  if (dep_info.computable != kComputable && !added_to_queue[dep_cindex_id]) {
163  added_to_queue[dep_cindex_id] = true;
164  cindexes_to_explain.push_back(dep_cindex_id);
165  }
166  if (iter+2 != end)
167  os << ", ";
168  }
169  os << "\n";
170  }
171  os << "\n";
172  KALDI_LOG << os.str();
173 }
174 
175 
176 void ComputationGraph::Print(std::ostream &os,
177  const std::vector<std::string> &node_names) {
178  int32 max_cindexes_per_line = 50, max_dependencies = 5,
179  num_cindexes = cindexes.size();
180 
181  std::vector<std::pair<Cindex, std::vector<Cindex> > > pairs;
182  pairs.reserve(num_cindexes);
183  for (int32 cindex_id = 0; cindex_id < num_cindexes; cindex_id++) {
184  int32 size = dependencies[cindex_id].size();
185  std::vector<Cindex> deps(size);
186  for (size_t i = 0; i < size; i++)
187  deps[i] = cindexes[dependencies[cindex_id][i]];
188  std::sort(deps.begin(), deps.end());
189  pairs.push_back(std::pair<Cindex, std::vector<Cindex> >(cindexes[cindex_id],
190  deps));
191  }
192  std::sort(pairs.begin(), pairs.end());
193  int32 cur_start = 0;
194  for (int32 i = 0; i < num_cindexes; i++) {
195  if (pairs[i].first.first != pairs[cur_start].first.first) {
196  cur_start = i;
197  os << "\n";
198  }
199  if (i - cur_start < max_cindexes_per_line) {
200  os << "[ ";
201  PrintCindex(os, pairs[i].first, node_names);
202  if (! is_input[GetCindexId(pairs[i].first)]) {
203  // only print out dependences for cindexes that
204  // were not provided as inputs.
205  os << " -> ";
206  for (int32 j = 0; j < pairs[i].second.size(); j++) {
207  if (j < max_dependencies) {
208  PrintCindex(os, pairs[i].second[j], node_names);
209  if (j + 1 < pairs[i].second.size())
210  os << ", ";
211  } else if (j == max_dependencies) {
212  os << "...";
213  }
214  }
215  }
216  os << " ] ";
217  } else if (i - cur_start == max_cindexes_per_line) {
218  os << "...";
219  }
220  }
221  os << "\n";
222 
223 }
224 
225 
226 // inline
228  // If this cindex_id has just now been added to the graph, the following
229  // assert should succeed.
230  KALDI_PARANOID_ASSERT(cindex_id == depend_on_this_.size() &&
231  cindex_id == cindex_info_.size());
232  depend_on_this_.push_back(std::vector<int32>());
233  cindex_info_.push_back(CindexInfo());
234 }
235 /*
236  CindexInfo &info = cindex_info_.back();
237  if (is_input) {
238  info.computable = k
239 
240  computable_info_.push_back(kComputable);
241  computable_queued_.push_back(false);
242  } else {
243  computable_info_.push_back(kUnknown);
244  // add to the queue of things for which we need to compute their computable
245  // status.
246  computable_queued_.push_back(false);
247  next_queue_.push_back(cindex_id);
248  }
249  depend_on_this_.
250  usable_count_.push_back(is_output ? 1 : 0);
251  }*/
252 
253 
255  int32 num_added = 0;
256  for (int32 i = 0; i < request_->inputs.size(); i++) {
257  int32 n = nnet_.GetNodeIndex(request_->inputs[i].name);
258  if (n == -1)
259  KALDI_ERR << "Network has no input with name "
260  << request_->inputs[i].name;
261  NodeType t = nnet_.GetNode(n).node_type;
262  KALDI_ASSERT((t == kInput || t == kComponent) &&
263  "Inputs to graph only allowed for Input and Component nodes.");
264 
265  for (int32 j = 0; j < request_->inputs[i].indexes.size(); j++) {
266  Cindex cindex(n, request_->inputs[i].indexes[j]);
267  bool is_input = true, is_new;
268  int32 cindex_id = graph_->GetCindexId(cindex, is_input, &is_new);
269  KALDI_ASSERT(is_new && "Input index seems to be listed more than once");
270  AddCindexId(cindex_id);
271  cindex_info_.back().computable = kComputable;
272  num_added++;
273  }
274  }
275  KALDI_ASSERT(num_added > 0 && "AddInputToGraph: nothing to add.");
276 }
277 
279  int32 num_added = 0;
280  for (int32 i = 0; i < request_->outputs.size(); i++) {
281  int32 n = nnet_.GetNodeIndex(request_->outputs[i].name);
282  if (n == -1)
283  KALDI_ERR << "Network has no output with name "
284  << request_->outputs[i].name;
285  for (int32 j = 0; j < request_->outputs[i].indexes.size(); j++) {
286  Cindex cindex(n, request_->outputs[i].indexes[j]);
287  bool is_input = false, is_new;
288  int32 cindex_id = graph_->GetCindexId(cindex, is_input, &is_new);
289  KALDI_ASSERT(is_new && "Output index seems to be listed more than once");
290  AddCindexId(cindex_id);
291  cindex_info_.back().usable_count = 1;
292  cindex_info_.back().queued = true;
293  next_queue_.push_back(cindex_id);
294  num_added++;
295  }
296  }
297  if (num_added == 0) {
298  KALDI_ERR << "Cannot process computation request with no outputs";
299  }
300  current_distance_ = 0;
301  KALDI_ASSERT(current_queue_.empty());
302  current_queue_.swap(next_queue_);
303 }
304 
306  auto iter = cindex_info_.begin(),
307  end = cindex_info_.end();
308  for (int32 cindex_id = 0; iter != end; ++iter, ++cindex_id) {
309  if (iter->computable != kComputable) {
310  int32 network_node = graph_->cindexes[cindex_id].first;
311  if (nnet_.IsOutputNode(network_node))
312  return false;
313  }
314  }
315  return true;
316 }
317 
318 std::ostream& operator << (std::ostream &os,
320  switch (info) {
321  case ComputationGraphBuilder::kUnknown: os << "kUnknown";
322  break;
323  case ComputationGraphBuilder::kComputable: os << "kComputable";
324  break;
325  case ComputationGraphBuilder::kNotComputable: os << "kNotComputable";
326  break;
327  default: os << "[invalid enum value]"; break;
328  }
329  return os;
330 }
331 
332 
333 // Prints logging info to explain why all outputs are not computable.
335  std::vector<int32> outputs_not_computable;
336  int32 num_outputs_total = 0;
337 
338  std::vector<Cindex>::const_iterator iter = graph_->cindexes.begin(),
339  end = graph_->cindexes.end();
340  for (int32 cindex_id = 0; iter != end; ++iter,++cindex_id) {
341  int32 network_node = iter->first;
342  if (nnet_.IsOutputNode(network_node)) {
343  num_outputs_total++;
344  if (cindex_info_[cindex_id].computable != kComputable)
345  outputs_not_computable.push_back(cindex_id);
346  }
347  }
348  KALDI_ASSERT(!outputs_not_computable.empty() &&
349  "You called this function when everything was computable.");
350  int32 num_print = 10, num_not_computable = outputs_not_computable.size();
351  KALDI_LOG << num_not_computable << " output cindexes out of "
352  << num_outputs_total << " were not computable.";
353  std::ostringstream os;
354  request_->Print(os);
355  KALDI_LOG << "Computation request was: " << os.str();
356  if (num_not_computable > num_print)
357  KALDI_LOG << "Printing the reasons for " << num_print << " of these.";
358  for (int32 i = 0; i < num_not_computable && i < num_print; i++)
359  ExplainWhyNotComputable(outputs_not_computable[i]);
360 }
361 
362 
363 
364 // this function limits the dependencies of cindex_id "cindex_id" to just those
365 // which are actually used in computing it. It also clears the dependencies
366 // of those cindexes that are not computable.
368  const CindexInfo &info = cindex_info_[cindex_id];
369  // by the time this is called, there should be no cindexes with unknown state
370  // that are usable.
371  KALDI_ASSERT(!(info.computable == kUnknown && info.usable_count != 0));
372  if (info.computable == kNotComputable || info.usable_count == 0) {
373  // if something is not computable or is not usable, there is no point
374  // keeping around its dependencies.
375  graph_->dependencies[cindex_id].clear();
376  return;
377  }
378  KALDI_ASSERT(info.computable == kComputable);
379  const Cindex &cindex = graph_->cindexes[cindex_id];
380  int32 node_id = cindex.first;
381  const Index &index = cindex.second;
382  const NetworkNode &node = nnet_.GetNode(node_id);
383 
384  std::vector<int32> &dependencies = graph_->dependencies[cindex_id];
385  std::sort(dependencies.begin(), dependencies.end());
386  std::vector<int32> used_cindex_ids;
387 
388  switch (node.node_type) {
389  case kDescriptor: {
390  const Descriptor &desc = node.descriptor;
391  bool dont_care = false; // there should be no kUnknown, and we check this
392  CindexSet cindex_set(*graph_, cindex_info_, dont_care);
393  std::vector<Cindex> used_cindexes;
394  bool ans = desc.IsComputable(index, cindex_set, &used_cindexes);
395  // If the next assert fails it could be a failure in the assumption that
396  // making more inputs available will never change something from not being
397  // computable to being computable; or it could be a bug elsewhere.
398  KALDI_ASSERT(ans);
399  size_t size = used_cindexes.size();
400  used_cindex_ids.resize(size);
401  for (size_t i = 0; i < size; i++) {
402  int32 dep_cindex_id = graph_->GetCindexId(used_cindexes[i]);
403  KALDI_ASSERT(dep_cindex_id != -1);
404  used_cindex_ids[i] = dep_cindex_id;
405  KALDI_ASSERT(std::binary_search(dependencies.begin(),
406  dependencies.end(),
407  dep_cindex_id));
408  }
409  break;
410  }
411  case kComponent: {
412  const Component *c = nnet_.GetComponent(node.u.component_index);
413  bool dont_care = false; // there should be no kUnknown, and we check this
414  // In the line below, node_id - 1 is the index of the component-input
415  // node-- the descriptor at the input to the component. We are interested
416  // in the set of inputs to the component that are computable.
417  IndexSet index_set(*graph_, cindex_info_, node_id - 1, dont_care);
418  std::vector<Index> used_indexes;
419  bool ans = c->IsComputable(request_->misc_info, index, index_set,
420  &used_indexes);
421  // If the next assert fails it could be a failure in the assumption that
422  // making more inputs available will never change something from not being
423  // computable to being computable; or it could be a bug elsewhere.
424  KALDI_ASSERT(ans);
425  size_t size = used_indexes.size();
426  used_cindex_ids.resize(size);
427  for (size_t i = 0; i < size; i++) {
428  Cindex dep_cindex(node_id - 1, used_indexes[i]);
429  int32 dep_cindex_id = graph_->GetCindexId(dep_cindex);
430  KALDI_ASSERT(dep_cindex_id != -1);
431  used_cindex_ids[i] = dep_cindex_id;
432  KALDI_ASSERT(std::binary_search(dependencies.begin(),
433  dependencies.end(),
434  dep_cindex_id));
435  }
436  break;
437  }
438  case kDimRange:
439  KALDI_ASSERT(dependencies.size() == 1);
440  // there should be exactly one dependency and it is required, not
441  // optional, so there is nothing to do here. Return.
442  return;
443  case kInput:
444  KALDI_ASSERT(dependencies.empty());
445  // there is nothing to do; return.
446  return;
447  default:
448  KALDI_ERR << "Invalid node type";
449  }
450  SortAndUniq(&used_cindex_ids);
451 
452  // the next statement modifies the graph.
453  dependencies.swap(used_cindex_ids);
454 }
455 
457  const Nnet &nnet,
458  ComputationGraph *graph):
459  nnet_(nnet), request_(NULL), graph_(graph),
460  current_distance_(-1) {
461  KALDI_ASSERT(graph_->cindexes.empty() &&
462  "ComputationGraphBuilder initialized with nonempty graph.");
463 }
464 
465 
467  if (request_ != NULL && graph_->segment_ends.empty()) {
468  // this check is relevant to multi-segment (i.e. online) computations.
469  KALDI_ERR << "You are calling things in the wrong order: should be "
470  << "Compute(), Prune(), Compute, Prune(), ...";
471  }
472  int32 cur_segment_start = graph_->cindexes.size();
473  request_ = &request;
474  AddInputs();
475  AddOutputs(); // sets current_distance_ to 0.
476  // max_distance for debugging, to detect infinite recursion.
477  int32 max_distance = 10000;
478  while (current_distance_ < max_distance) {
480  // only check rarely if we're running at low verbose level.
481  if (GetVerboseLevel() >= 3 || RandInt(1, (current_distance_ + 1)) == 1)
482  Check(cur_segment_start);
483  if (current_queue_.empty()) // we're done.
484  break;
485  }
486  KALDI_VLOG(6) << "current_distance = " << current_distance_; // TEMP
487  if (current_distance_ == max_distance)
488  KALDI_ERR << "Loop detected while building computation graph (bad "
489  << "network topology?)";
490 
491  if (RandInt(1, 2 * (graph_->segment_ends.size() + 1)) == 1)
492  Check(cur_segment_start);
493 }
494 
495 
496 void ComputationGraphBuilder::Check(int32 start_cindex_id) const {
497  int32 num_cindex_ids = graph_->cindexes.size();
498  for (int32 cindex_id = start_cindex_id; cindex_id < num_cindex_ids;
499  cindex_id += 1 + RandInt(0, num_cindex_ids / 100)) {
500  { // check depend_on_this.
501  std::vector<int32> depend_on_this = depend_on_this_[cindex_id];
502  int32 size = depend_on_this.size();
503  std::sort(depend_on_this.begin(), depend_on_this.end());
504  KALDI_ASSERT(IsSortedAndUniq(depend_on_this));
505  for (size_t j = 0; j < size; j++) {
506  int32 other_cindex_id = depend_on_this[j];
507  // make sure appears in appropriate dependencies array.
508  const std::vector<int32> &dep = graph_->dependencies[other_cindex_id];
509  KALDI_ASSERT(std::count(dep.begin(), dep.end(), cindex_id) == 1);
510  }
511  }
512  if (cindex_info_[cindex_id].dependencies_computed) {
513  // check dependencies.
514  std::vector<int32> dependencies = graph_->dependencies[cindex_id];
515  int32 size = dependencies.size();
516  std::sort(dependencies.begin(), dependencies.end());
517  KALDI_ASSERT(IsSortedAndUniq(dependencies));
518  for (size_t j = 0; j < size; j++) {
519  int32 dep_cindex_id = dependencies[j];
520  if (dep_cindex_id >= start_cindex_id) {
521  // make sure appears in appropriate depend_on_this_ array.
522  const std::vector<int32> &dep = depend_on_this_[dep_cindex_id];
523  KALDI_ASSERT(std::count(dep.begin(), dep.end(), cindex_id) == 1);
524  }
525  }
526  }
527 
528  {
529  // check usable_count_
530  int32 node_index = graph_->cindexes[cindex_id].first;
531  int32 usable_count = cindex_info_[cindex_id].usable_count,
532  usable_count_recomputed = nnet_.IsOutputNode(node_index) ? 1 : 0;
533  std::vector<int32> depend_on_this = depend_on_this_[cindex_id];
534  int32 size = depend_on_this.size();
535  for (size_t j = 0; j < size; j++) {
536  int32 other_cindex_id = depend_on_this[j];
537  if (cindex_info_[other_cindex_id].usable_count != 0 &&
538  cindex_info_[other_cindex_id].computable != kNotComputable)
539  usable_count_recomputed++;
540  }
541  KALDI_ASSERT(usable_count == usable_count_recomputed);
542  }
543  // check `computable`.
544  if (cindex_info_[cindex_id].dependencies_computed) {
546  ComputeComputableInfo(cindex_id);
547  // the status doesn't have to match if the stored info is kUnknown.
548  if (c != cindex_info_[cindex_id].computable &&
549  cindex_info_[cindex_id].computable != kUnknown) {
550  KALDI_ERR << "Mismatch in computable status";
551  }
552  }
553  // check `queued`.
554  // note, the following checks might be a bit slow.
555  if (RandInt(0, cindex_id) == 0) {
556  if (cindex_info_[cindex_id].queued) {
558  current_queue_.end(),
559  cindex_id) == 1);
560  } else {
562  current_queue_.end(),
563  cindex_id) == 0);
564  }
565  }
566  }
567 }
568 
570  // Since Prune() is called for each segment in turn [note: there
571  // will be only 1 segment in the normal non-online case], we
572  // only prune for the current, just-added segment.
573  int32 start_cindex_id = (graph_->segment_ends.empty() ? 0 :
574  graph_->segment_ends.back());
575  int32 num_cindex_ids = graph_->cindexes.size();
576  // Prune the dependencies to just those that are used (to remove
577  // optional dependencies that don't end up getting used).
578 
579  for (int32 cindex_id = start_cindex_id;
580  cindex_id < num_cindex_ids; cindex_id++)
581  PruneDependencies(cindex_id);
582  // the following clears the elements of depend_on_this from start_cindex_id to
583  // num_cindex_ids - 1, without touching the entire array.
584  depend_on_this_.resize(start_cindex_id);
585  depend_on_this_.resize(num_cindex_ids);
586  std::vector<bool> required;
587  ComputeRequiredArray(start_cindex_id, &required);
588 
589  std::vector<bool> keep(num_cindex_ids - start_cindex_id, false);
590  for (int32 c = start_cindex_id; c < num_cindex_ids; c++) {
591  if (required[c - start_cindex_id] || graph_->is_input[c]) {
592  KALDI_ASSERT(cindex_info_[c].computable == kComputable &&
593  "You are calling Prune when not everything is computable.");
594  keep[c - start_cindex_id] = true;
595  }
596  }
597  graph_->Renumber(start_cindex_id, keep);
598  // We also need to renumber cindex_info_ b,
599  // graph_->Renumber doesn't do for us, but we can make some shortcuts. We set
600  // all computable_info_ to kComputable because actually it all was kComputable
601  // (we checked when deciding what to keep); and we set the usable_count_ to 1
602  // for all the cindex_ids we just added... this is not 100% accurate
603  // according to the way we defined usable_count_, but it prevents additional
604  // computation since it is > 0 (notice that IncrementUsableCount and
605  // DecrementUsableCount may do some work when the usable_count goes to zero or
606  // from zero. Anyway, the usable-count for these cindex_ids for those "older
607  // segments" is not critical. [this information only gets used if we process
608  // additional segments as part of the compilation of an online computation.]
609  int32 new_num_cindex_ids = graph_->cindexes.size();
610  cindex_info_.resize(start_cindex_id);
611  cindex_info_.resize(new_num_cindex_ids);
612  for (int32 i = start_cindex_id; i < new_num_cindex_ids; i++) {
613  cindex_info_[i].computable = kComputable;
614  cindex_info_[i].usable_count = 1;
615  }
616  // depend_on_this_ is a vector of vectors-- keeping track of the reverse of
617  // the dependencies-- and I believe we won't be needing this information any
618  // more past this point.
619  depend_on_this_.resize(start_cindex_id);
620  depend_on_this_.resize(new_num_cindex_ids);
621 
622  graph_->segment_ends.push_back(new_num_cindex_ids);
623 }
624 
625 // Add cindex_ids that this cindex_id depends on.
627  if (static_cast<int32>(graph_->dependencies.size()) <= cindex_id) {
628  graph_->dependencies.resize(2 * cindex_id + 1);
629  }
630  Cindex cindex = graph_->cindexes[cindex_id];
631 
632  // find the dependencies of this cindex.
633  int32 node_index = cindex.first;
634  const Index &index = cindex.second;
635  const NetworkNode &node = nnet_.GetNode(node_index);
636 
637  std::vector<Cindex> input_cindexes;
638 
639  // the following switch statement sets up "input_cindexes".
640  switch (node.node_type) {
641  case kDescriptor: {
642  // desc describes how this node obtains its input from other nodes.
643  const Descriptor &desc = node.descriptor;
644  desc.GetDependencies(index, &input_cindexes);
645  break;
646  }
647  case kComponent: {
648  int32 c = node.u.component_index;
649  const Component *component = nnet_.GetComponent(c);
650  std::vector<Index> input_indexes;
651  component->GetInputIndexes(request_->misc_info, index,
652  &input_indexes);
653  input_cindexes.resize(input_indexes.size());
654  for (size_t i = 0; i < input_indexes.size(); i++) {
655  input_cindexes[i].first = node_index - 1; // preceding node
656  input_cindexes[i].second = input_indexes[i];
657  }
658  break;
659  }
660  case kDimRange: {
661  input_cindexes.resize(1);
662  input_cindexes[0] = Cindex(node.u.node_index, index);
663  break;
664  }
665  case kInput:
666  break; // There will be no dependencies.
667  default:
668  KALDI_ERR << "Invalid node type";
669  }
670 
671  int32 num_dependencies = input_cindexes.size();
672  // this "reserve" statement is to make sure the reference
673  // we declare below does not become invalid in the loop below
674  // (the call to graph_->GetCindexId() could add up to
675  // num_dependencies elements to the graph_->dependencies array
676  // and we want to avoid allocation).
677  // the RoundUpToNearestPowerOfTwo is for efficiency, to
678  // avoid too-frequent resizes.
680  graph_->dependencies.size() + num_dependencies));
681  std::vector<int32> &this_dep = graph_->dependencies[cindex_id];
682 
683  this_dep.resize(num_dependencies);
684  for (size_t i = 0; i < num_dependencies; i++) {
685  bool is_input = false, is_new;
686  int32 dep_cindex_id = graph_->GetCindexId(input_cindexes[i],
687  is_input, &is_new);
688  this_dep[i] = dep_cindex_id;
689  if (is_new) {
690  AddCindexId(dep_cindex_id);
691  cindex_info_.back().queued = true;
692  next_queue_.push_back(dep_cindex_id);
693  }
694  // we will keep dependent's usable_count_ up to date below
695  }
696  // remove duplicates of dependencies.
697  SortAndUniq(&this_dep);
698  // set up the "depend_on_this_" array.
699  std::vector<int32>::const_iterator iter = this_dep.begin(),
700  end = this_dep.end();
701 
702  // Populate the "depend_on_this_" array, and append the
703  // usable_count_ of things we depend on (see the definition
704  // of this quantity next to where it is declared).
705  // Note: before calling AddDependencies() we verified the following:
706  // computable_info_[cindex_id] == kUnknown
707  // and
708  // usable_count_[cindex_id] != 0
709  // which ensures that the conditions to increment the dependency's
710  // usable_count_ are satisfied.
711  for (; iter != end; ++iter) {
712  int32 dep_cindex_id = *iter;
713  depend_on_this_[dep_cindex_id].push_back(cindex_id);
714  IncrementUsableCount(dep_cindex_id);
715  }
716 }
717 
718 
721  const {
722  const Cindex &cindex = graph_->cindexes[cindex_id];
723  int32 node_id = cindex.first;
724  const Index &index = cindex.second;
725  const NetworkNode &node = nnet_.GetNode(node_id);
726  switch (node.node_type) {
727  case kDescriptor: {
728  const Descriptor &desc = node.descriptor;
729  {
730  CindexSet cindex_set(*graph_, cindex_info_, false);
731  if (desc.IsComputable(index, cindex_set, NULL)) {
732  // it's computable even without counting kUnknown and kWillNotCompute
733  // inputs as computable [treat_unknown_as_computable = false] ->
734  // definitely computable.
735  return kComputable;
736  }
737  }
738  CindexSet cindex_set2(*graph_, cindex_info_, true);
739  if (!desc.IsComputable(index, cindex_set2, NULL)) {
740  // it's not computable even when counting kUnknown
741  // inputs as computable [treat_unknown_as_computable = true] ->
742  // definitely not computable.
743  return kNotComputable;
744  }
745  return kUnknown;
746  }
747  case kComponent: {
748  const Component *c = nnet_.GetComponent(node.u.component_index);
749  const int32 input_node_id = node_id - 1;
750  {
751  IndexSet index_set(*graph_, cindex_info_, input_node_id, false);
752  if (c->IsComputable(request_->misc_info, index, index_set, NULL)) {
753  // it's computable even without counting kUnknown
754  // inputs as computable [treat_unknown_as_computable = false] ->
755  // definitely computable.
756  return kComputable;
757  }
758  }
759  IndexSet index_set2(*graph_, cindex_info_, input_node_id, true);
760  if (!c->IsComputable(request_->misc_info, index, index_set2, NULL)) {
761  // it's not computable even when counting kUnknown inputs as computable
762  // [treat_unknown_as_computable = true] -> definitely not computable.
763  return kNotComputable;
764  }
765  return kUnknown;
766  }
767  case kDimRange: {
768  Cindex input_cindex(node.u.node_index, index);
769  int32 input_cindex_id = graph_->GetCindexId(input_cindex);
770  if (input_cindex_id != -1)
771  return cindex_info_[input_cindex_id].computable;
772  else
773  return kUnknown;
774  }
775  case kInput: {
776  // cindexes for input nodes that are part of the computation request will
777  // have graph_->is_input[cindex_id] == true; others will have
778  // graph_->is_input[cindex_id] == true.
779  return graph_->is_input[cindex_id] ? kComputable : kNotComputable;
780  }
781  default:
782  KALDI_ERR << "Invalid node type.";
783  return kUnknown; // suppress compiler warning.
784  }
785 }
786 
788  std::vector<std::vector<bool> > *computable) const {
789  KALDI_ASSERT(!graph_->cindexes.empty() &&
790  "You need to call this after Compute()!");
791  KALDI_ASSERT(!cindex_info_.empty() &&
792  "You need to call this before Prune()!");
793  computable->clear();
794  computable->resize(request_->outputs.size());
795  for (size_t i = 0; i < request_->outputs.size(); i++) {
796  const IoSpecification &output = request_->outputs[i];
797  int32 n = nnet_.GetNodeIndex(output.name);
798  KALDI_ASSERT(n != -1);
799  int32 size = output.indexes.size();
800  std::vector<bool> &this_vec = (*computable)[i];
801  this_vec.resize(size);
802  for (size_t j = 0; j < size; j++) {
803  Cindex cindex(n, output.indexes[j]);
804  int32 cindex_id = graph_->GetCindexId(cindex);
805  KALDI_ASSERT(cindex_id != -1);
806  this_vec[j] = (cindex_info_[cindex_id].computable == kComputable);
807  }
808  }
809 }
810 
811 
813  // if the current computable_info_ for cindex_id value is not kUnknown, this
814  // cindex_id should not have been in the queue.
815  KALDI_ASSERT(static_cast<size_t>(cindex_id) < cindex_info_.size());
816 
817  // We don't need to update the computable info of this node since it's
818  // not usable (i.e. not currently reachable from any node that is not
819  // kNotComputable).
820  if (cindex_info_[cindex_id].usable_count == 0)
821  return;
822 
823  ComputableInfo &output = cindex_info_[cindex_id].computable;
824  KALDI_ASSERT(output == kUnknown);
825 
826  output = ComputeComputableInfo(cindex_id);
827 
828  if (output != kUnknown) {
829  // The computable status of cindexes that depend on this cindex and whose
830  // status is currently kUnknown might now change, so if they are not in the
831  // computable queue, put them there.
832  std::vector<int32>::const_iterator iter = depend_on_this_[cindex_id].begin(),
833  end = depend_on_this_[cindex_id].end();
834  for (; iter != end; ++iter) {
835  int32 other_cindex_id = *iter;
836  if (cindex_info_[other_cindex_id].computable == kUnknown &&
837  !cindex_info_[other_cindex_id].queued) {
838  cindex_info_[other_cindex_id].queued = true;
839  next_queue_.push_back(other_cindex_id);
840  }
841  }
842  if (output == kNotComputable && cindex_info_[cindex_id].usable_count != 0) {
843  // If we have just changed the computable state from kUnknown to
844  // kNotComputable, then given the way the usable_count is defined (see
845  // the declaration), this means that we must decrement the
846  // usable_count_ of all cindex_ids that we depend on.
847  std::vector<int32>::const_iterator
848  iter = graph_->dependencies[cindex_id].begin(),
849  end = graph_->dependencies[cindex_id].end();
850  for (; iter != end; ++iter) {
851  int32 dep_cindex_id = *iter;
852  DecrementUsableCount(dep_cindex_id);
853  }
854  }
855  }
856 }
857 
858 
860  CindexInfo &info = cindex_info_[cindex_id];
861  if (info.usable_count++ == 0 &&
862  info.computable != kNotComputable) {
863  std::vector<int32>::const_iterator
864  iter = graph_->dependencies[cindex_id].begin(),
865  end = graph_->dependencies[cindex_id].end();
866  for (; iter != end; ++iter) {
867  int32 dep_cindex_id = *iter;
868  IncrementUsableCount(dep_cindex_id);
869  }
870  if (info.computable == kUnknown &&
871  !info.queued) {
872  // It's become usable, so make sure it's queued to process whether it is
873  // computable or not.
874  info.queued = true;
875  next_queue_.push_back(cindex_id);
876  }
877  }
878 }
879 
880 
882  KALDI_PARANOID_ASSERT(cindex_info_[cindex_id].usable_count > 0);
883  if (--cindex_info_[cindex_id].usable_count == 0 &&
884  cindex_info_[cindex_id].computable != kNotComputable) {
885  std::vector<int32>::const_iterator
886  iter = graph_->dependencies[cindex_id].begin(),
887  end = graph_->dependencies[cindex_id].end();
888  for (; iter != end; ++iter) {
889  int32 dep_cindex_id = *iter;
890  DecrementUsableCount(dep_cindex_id);
891  }
892  }
893 }
894 
895 
897  while (!current_queue_.empty()) {
898  int32 cindex_id = current_queue_.back();
899  current_queue_.pop_back();
900  cindex_info_[cindex_id].queued = false;
901  if (!cindex_info_[cindex_id].dependencies_computed &&
902  cindex_info_[cindex_id].usable_count != 0) {
903  cindex_info_[cindex_id].dependencies_computed = true;
904  AddDependencies(cindex_id);
905  // Add to the queue so we can check whether it's computable.
906  if (!cindex_info_[cindex_id].queued) {
907  cindex_info_[cindex_id].queued = true;
908  next_queue_.push_back(cindex_id);
909  }
910  } else if (cindex_info_[cindex_id].computable == kUnknown) {
911  UpdateComputableInfo(cindex_id);
912  }
913  }
914  current_queue_.swap(next_queue_); // now next_queue_ will be empty.
916 }
917 
919  int32 start_cindex_id,
920  std::vector<bool> *required) const {
921 
922  int32 num_cindex_ids = graph_->cindexes.size();
923  KALDI_ASSERT(num_cindex_ids >= start_cindex_id);
924  KALDI_ASSERT(cindex_info_.size() == num_cindex_ids);
925  required->clear();
926  required->resize(num_cindex_ids - start_cindex_id, false);
927 
928  // would be bool, but indexing c++ bool may be slow.
929  std::vector<char> is_output_node(nnet_.NumNodes());
930  for (int32 n = 0; n < nnet_.NumNodes(); n++)
931  is_output_node[n] = (char)(nnet_.IsOutputNode(n) ? 1 : 0);
932 
933  std::vector<int32> queue;
934  for (int32 c = start_cindex_id; c < num_cindex_ids; c++) {
935  // First put the output cindex_ids into the queue.
936  int32 node_id = graph_->cindexes[c].first;
937  if (is_output_node[node_id]) {
938  (*required)[c - start_cindex_id] = true;
939  queue.push_back(c);
940  }
941  }
942  while (!queue.empty()) {
943  int32 c = queue.back();
944  queue.pop_back();
945  const std::vector<int32> &dependencies = graph_->dependencies[c];
946  std::vector<int32>::const_iterator iter = dependencies.begin(),
947  end = dependencies.end();
948  for (; iter != end; ++iter) {
949  int32 d = *iter;
950  if (d >= start_cindex_id && !(*required)[d - start_cindex_id]){
951  (*required)[d - start_cindex_id] = true;
952  queue.push_back(d);
953  }
954  }
955  }
956  // just check that we don't have any cindex_ids which are required but have
957  // usable_count_ == 0; this would indicate a bug somewhere.
958  for (int32 c = start_cindex_id; c < num_cindex_ids; c++)
959  KALDI_ASSERT(!((*required)[c - start_cindex_id] &&
960  (cindex_info_[c].usable_count == 0)));
961 
962 }
963 
964 
965 // make our own namespace for helper functions of ComputeComputationGraph.
966 namespace computation_graph {
967 
968 
969 // This function adds cindex_ids corresponding to each output
970 // index, to the graph.
972  const Nnet &nnet,
973  ComputationGraph *graph) {
974  int32 num_added = 0;
975  for (int32 i = 0; i < request.outputs.size(); i++) {
976  int32 n = nnet.GetNodeIndex(request.outputs[i].name);
977  if (n == -1)
978  KALDI_ERR << "Network has no output with name "
979  << request.outputs[i].name;
980  for (int32 j = 0; j < request.outputs[i].indexes.size(); j++) {
981  Cindex cindex(n, request.outputs[i].indexes[j]);
982  bool is_input = false, is_new;
983  graph->GetCindexId(cindex, is_input, &is_new); // ignore the return value.
984  KALDI_ASSERT(is_new && "Output index seems to be listed more than once");
985  num_added++;
986  }
987  }
988  KALDI_ASSERT(num_added > 0 && "AddOutputToGraph: nothing to add.");
989 }
990 
991 
992 // This function adds cindex_ids corresponding to each input index, to the
993 // graph.
995  const Nnet &nnet,
996  ComputationGraph *graph) {
997  int32 num_added = 0;
998  for (int32 i = 0; i < request.inputs.size(); i++) {
999  int32 n = nnet.GetNodeIndex(request.inputs[i].name);
1000  if (n == -1)
1001  KALDI_ERR << "Network has no input with name "
1002  << request.inputs[i].name;
1003  NodeType t = nnet.GetNode(n).node_type;
1004  KALDI_ASSERT((t == kInput || t == kComponent) &&
1005  "Inputs to graph only allowed for Input and Component nodes.");
1006 
1007  for (int32 j = 0; j < request.inputs[i].indexes.size(); j++) {
1008  Cindex cindex(n, request.inputs[i].indexes[j]);
1009  bool is_input = true, is_new;
1010  graph->GetCindexId(cindex, is_input, &is_new); // ignore the return value.
1011  KALDI_ASSERT(is_new && "Input index seems to be listed more than once");
1012  num_added++;
1013  }
1014  }
1015  KALDI_ASSERT(num_added > 0 && "AddInputToGraph: nothing to add.");
1016 }
1017 
1018 
1029  const ComputationGraph &graph,
1030  const std::vector<int32> &cindex_id_to_segment_and_epoch,
1031  std::vector<std::vector<int32> > *dependencies_subset) {
1032  int32 num_cindex_ids = graph.cindexes.size();
1033  KALDI_ASSERT(cindex_id_to_segment_and_epoch.size() == num_cindex_ids);
1034  dependencies_subset->resize(num_cindex_ids);
1035  for (int32 cindex_id = 0; cindex_id < num_cindex_ids; cindex_id++) {
1036  int32 phase_index = cindex_id_to_segment_and_epoch[cindex_id];
1037  const std::vector<int32> &dependencies = graph.dependencies[cindex_id];
1038  std::vector<int32> &dep_subset = (*dependencies_subset)[cindex_id];
1039  int32 num_dep = dependencies.size();
1040  for (int32 i = 0; i < num_dep; i++) {
1041  int32 d = dependencies[i];
1042  if (cindex_id_to_segment_and_epoch[d] == phase_index)
1043  dep_subset.push_back(d);
1044  }
1045  }
1046 }
1047 
1081 
1082 static void ComputeEpochInfo(
1083  const Nnet &nnet,
1084  const ComputationGraph &graph,
1085  std::vector<int32> *cindex_id_to_segment_and_epoch,
1086  std::vector<std::vector<std::vector<int32 > > > *epochs_per_segment,
1087  std::vector<bool> *epoch_is_trivial) {
1088 
1089  // node_to_epoch maps each nnet node to an index >= 0 that tells us coarsely
1090  // what order to compute them in... but we may need to compute a finer
1091  // ordering at the cindex_id level in cases like RNNs.
1092  std::vector<int32> node_to_epoch;
1093  ComputeNnetComputationEpochs(nnet, &node_to_epoch);
1094  {
1095  std::ostringstream os;
1096  PrintIntegerVector(os, node_to_epoch);
1097  KALDI_VLOG(6) << "node_to_epoch: " << os.str();
1098  }
1099 
1100  // Add one to the epoch numbering because we will be reserving
1101  // zero for inputs to the network, and we don't want to have to
1102  // prove that epoch number 0 would correspond only to inputs.
1103  for (int32 i = 0; i < node_to_epoch.size(); i++)
1104  node_to_epoch[i]++;
1105  int32 num_nodes = nnet.NumNodes(),
1106  num_cindex_ids = graph.cindexes.size(),
1107  num_segments = graph.segment_ends.size(),
1108  num_epoch_indexes = 1 + *std::max_element(node_to_epoch.begin(),
1109  node_to_epoch.end());
1110  KALDI_ASSERT(node_to_epoch.size() == num_nodes);
1111 
1112  epochs_per_segment->clear();
1113  epochs_per_segment->resize(num_segments);
1114 
1115  // epoch_to_num_nodes is only used so we know whether each epoch
1116  // index corresponds to multiple nodes; if it's just one node then we know
1117  // the computation is very simple and we can do an optimization.
1118  std::vector<int32> epoch_to_num_nodes(num_epoch_indexes, 0);
1119  for (int32 n = 0; n < num_nodes; n++)
1120  epoch_to_num_nodes[node_to_epoch[n]]++;
1121 
1122  epoch_is_trivial->resize(num_epoch_indexes);
1123  for (int32 o = 0; o < num_epoch_indexes; o++) {
1124  KALDI_ASSERT(o == 0 || epoch_to_num_nodes[o] > 0);
1125  (*epoch_is_trivial)[o] = (epoch_to_num_nodes[o] <= 1);
1126  }
1127  cindex_id_to_segment_and_epoch->resize(num_cindex_ids);
1128  KALDI_ASSERT(graph.segment_ends.back() == num_cindex_ids);
1129  int32 cur_segment_start = 0, cur_segment_end;
1130  for (int32 segment = 0; segment < num_segments; segment++) {
1131  cur_segment_end = graph.segment_ends[segment];
1132  std::vector<std::vector<int32> > &epochs = (*epochs_per_segment)[segment];
1133  epochs.resize(num_epoch_indexes);
1134 
1135  for (int32 cindex_id = cur_segment_start;
1136  cindex_id < cur_segment_end; cindex_id++) {
1137  int32 node_index = graph.cindexes[cindex_id].first,
1138  epoch_index = (graph.is_input[cindex_id] ? 0 :
1139  node_to_epoch[node_index]);
1140  (*cindex_id_to_segment_and_epoch)[cindex_id] =
1141  epoch_index + segment * num_epoch_indexes;
1142  epochs[epoch_index].push_back(cindex_id);
1143  }
1144  cur_segment_start = cur_segment_end;
1145  }
1146 }
1147 
1148 
1149 } // end namespace computation_graph
1150 
1151 
1153  const ComputationRequest &request,
1154  ComputationGraph *graph) {
1155  using namespace computation_graph;
1156  // make sure graph is empty at the start.
1157  KALDI_ASSERT(graph->cindexes.empty());
1158 
1159  AddInputToGraph(request, nnet, graph);
1160  AddOutputToGraph(request, nnet, graph);
1161 
1162  // queue of cindex_ids to process.
1163  std::vector<int32> queue(graph->cindexes.size());
1164  for (int32 i = 0; i < graph->cindexes.size(); i++)
1165  queue.push_back(i);
1166 
1167  while (!queue.empty()) {
1168  int32 cindex_id = queue.back();
1169  queue.pop_back();
1170  if (static_cast<int32>(graph->dependencies.size()) <= cindex_id)
1171  graph->dependencies.resize(cindex_id + 1);
1172 
1173  if (graph->is_input[cindex_id])
1174  continue;
1175  Cindex cindex = graph->cindexes[cindex_id];
1176 
1177  // find the dependencies of this cindex.
1178  int32 n = cindex.first;
1179  const Index &index = cindex.second;
1180  const NetworkNode &node = nnet.GetNode(n);
1181 
1182  std::vector<Cindex> input_cindexes;
1183 
1184  // the following switch statement sets up "input_cindexes".
1185  switch (node.node_type) {
1186  case kDescriptor: {
1187  // desc describes how this node obtains its input from other nodes.
1188  const Descriptor &desc = node.descriptor;
1189  desc.GetDependencies(index, &input_cindexes);
1190  break;
1191  }
1192  case kComponent: {
1193  int32 c = node.u.component_index;
1194  const Component *component = nnet.GetComponent(c);
1195  std::vector<Index> input_indexes;
1196  component->GetInputIndexes(request.misc_info, index,
1197  &input_indexes);
1198  // each Component node should be preceded by a node that describes its
1199  // input, of type kDescriptor
1200  KALDI_ASSERT(nnet.GetNode(n-1).node_type ==
1201  kDescriptor);
1202 
1203  input_cindexes.resize(input_indexes.size());
1204  for (size_t i = 0; i < input_indexes.size(); i++) {
1205  input_cindexes[i].first = n - 1; // preceding node.
1206  input_cindexes[i].second = input_indexes[i];
1207  }
1208  break;
1209  }
1210  case kDimRange: {
1211  input_cindexes.resize(1);
1212  input_cindexes[0] = Cindex(node.u.node_index, index);
1213  break;
1214  }
1215  case kInput: default:
1216  // for kInput, you should have hit the "continue" statement above.
1217  KALDI_ERR << "Invalid node type";
1218  }
1219  std::vector<int32> &this_dep = graph->dependencies[cindex_id];
1220 
1221  int32 num_dependencies = input_cindexes.size();
1222  this_dep.resize(num_dependencies);
1223  for (size_t i = 0; i < num_dependencies; i++) {
1224  bool is_input = false, is_new;
1225  int32 dep_cindex_id = graph->GetCindexId(input_cindexes[i],
1226  is_input, &is_new);
1227  this_dep[i] = dep_cindex_id;
1228  if (is_new)
1229  queue.push_back(dep_cindex_id);
1230  }
1231 
1232  // remove duplicates of dependencies.
1233  SortAndUniq(&this_dep);
1234  }
1235 }
1236 
1237 
1238 static int32 SumVectorSizes(const std::vector<std::vector<int32> > &vec) {
1239  int32 ans = 0;
1240  std::vector<std::vector<int32> >::const_iterator iter = vec.begin(),
1241  end = vec.end();
1242  for (; iter != end; ++iter)
1243  ans += iter->size();
1244  return ans;
1245 }
1246 
1247 static int32 SumVectorSizes(const std::vector<std::vector<std::vector<int32> > > &vec) {
1248  int32 ans = 0;
1249  for (size_t i = 0; i < vec.size(); i++)
1250  ans += SumVectorSizes(vec[i]);
1251  return ans;
1252 }
1253 
1254 
1255 /*
1256  this function is called from ComputeComputationPhases; it handles the part of
1257  the computation from one epoch (this code was broken out to avoid that
1258  function being super-long). Note: the phases are a numbered grouping of
1259  cindexes that say in what order we compute things, i.e. we first compute
1260  all the cindexes for phase 0, then for phase 1, and so on.
1261 
1262  @param [in] nnet The neural net this computation is for
1263  @param [in] graph The computation graph we're computing the phases for.
1264 
1265  @param [in] this_epoch The sorted list of the cindex_ids for this epoch; note,
1266  cindex_ids are indexes into the array graph.cindexes.
1267  Roughly speaking, this is a list of the cindex_ids that
1268  correspond to one "layer" of the neural network, in
1269  things like LSTMs, or for one part of one layer (the
1270  affine component, the nonlinearity, or the splicing),
1271  in things like TDNNs.
1272  @param [in] dependencies_subset A subset of 'graph.dependencies' corresponding
1273  just to dependencies within the same epoch (not specifically
1274  this epoch; for all epochs). In general, for a cindex_id c
1275  dependencies[c] is a list of other cindex_ids d1, d2,
1276  such that in order to compute c we must first compute
1277  d1, d2 and so on (plus d1, d2, etc. must be from the
1278  same epoch as c).
1279  @param [in] depends_on_subset The graph-transpose of dependencies_subset;
1280  for cindex_id c, depends_on_subset[c] is the list
1281  of cindex_ids that directly depend on cindex_id c,
1282  so c must be computed before them.
1283  @param [in] epoch_is_trivial A bool that's true if this epoch is trivial
1284  (meaning it consists of just one component)... this
1285  enables a faster code path in this common case.
1286  @param [in,out] phase_indexes This vector, to some elements of which this
1287  function writes each time it is called, maps from
1288  cindex_id to the 'phase index'. A phase index is a
1289  number identifying the phases [like coarse steps] of
1290  the computation, with zero for the first phase, one
1291  for the second, etc. We work out how many phase
1292  indexes have been used already by previous epochs,
1293  from phases->size(). Actually, phase_indexes is
1294  really just a temporary variable used by this
1295  function, that we allocate outside this function for
1296  efficiency. It is initialized to -1 outside this
1297  function; different invocations of this function work
1298  with different non-overlapping elements of the vector.
1299  @param [in,out] phases This is the output of this
1300  function. Each time we add a new phase, we append a
1301  vector to *phases. E.g. (*phases)[0] is the sorted
1302  list of cindexes in the first phase of the
1303  computation... and so on. Note, this function is
1304  called multiple times, and each time we add one or
1305  more phases to this vector, so its size grows.
1306 */
1308  const Nnet &nnet,
1309  const ComputationGraph &graph,
1310  const std::vector<int32> &this_epoch,
1311  const std::vector<std::vector<int32> > &dependencies_subset,
1312  const std::vector<std::vector<int32> > &depend_on_subset,
1313  bool epoch_is_trivial,
1314  std::vector<int32> *phase_indexes,
1315  std::vector<std::vector<int32> > *phases) {
1316  std::vector<int32> this_phase, next_phase_candidates;
1317 
1318  if (this_epoch.empty())
1319  return;
1320 
1321  if (epoch_is_trivial) { // an optimization
1322  this_phase = this_epoch;
1323  } else {
1324  // Start out with all elements of this epoch that have no
1325  // dependencies within the same epoch (i.e. those that
1326  // can be computed first).
1327  std::vector<int32>::const_iterator iter = this_epoch.begin(),
1328  end = this_epoch.end();
1329  for (; iter != end; ++iter) {
1330  int32 cindex_id = *iter;
1331  if (dependencies_subset[cindex_id].empty())
1332  this_phase.push_back(cindex_id);
1333  }
1334  }
1335 
1336  // if the next assert fails, the graph at the level of cindex_ids is not acyclic.
1337  KALDI_ASSERT(!this_phase.empty() &&
1338  "Trying to process computation with cycles");
1339 
1340  while (!this_phase.empty()) {
1341  // The next two lines are a more efficient version of:
1342  // phases->push_back(this_phase);
1343  phases->resize(phases->size() + 1);
1344  phases->back().swap(this_phase);
1345  // The next if-statement is an optimization: if for this epoch index
1346  // there is just one node, we can skip the rest of this loop. Note: if
1347  // epoch == 0, even if there is just one node, cindex_ids from
1348  // multiple nodes may be put here because of the rule that cindex_ids which
1349  // are inputs always get epoch 0. But it's still true that they
1350  // will have no dependencies, so we can still skip the code below.
1351  if (epoch_is_trivial)
1352  return;
1353 
1354  int32 cur_phase_index = phases->size() - 1;
1355 
1356  // next_phases_candidates is a list of cindexes that we should check
1357  // whether they are computable now, because one of the things they depend
1358  // on just became computable.
1359  next_phase_candidates.clear();
1360  std::vector<int32>::const_iterator this_phase_iter = phases->back().begin(),
1361  this_phase_end = phases->back().end();
1362 
1363  for (; this_phase_iter != this_phase_end; ++this_phase_iter) {
1364  int32 c = *this_phase_iter; // c is a cindex_id with phase cur_phase_index.
1365  (*phase_indexes)[c] = cur_phase_index;
1366  std::vector<int32>::const_iterator iter = depend_on_subset[c].begin(),
1367  end = depend_on_subset[c].end();
1368  for (; iter != end; ++iter) {
1369  int32 d = *iter; // cindex_id that depends on c.
1370  next_phase_candidates.push_back(d);
1371  }
1372  }
1373  SortAndUniq(&next_phase_candidates);
1374  // note, at this point 'this_phase' will be the empty vector [see the 'swap'
1375  // above].
1376  this_phase.reserve(next_phase_candidates.size());
1377  // now check the candidates that might be in the next phase, and put any
1378  // members that we are currently able to compute into "this_phase".
1379  std::vector<int32>::const_iterator iter = next_phase_candidates.begin(),
1380  end = next_phase_candidates.end();
1381  for (; iter != end; ++iter) {
1382  int32 c = *iter;
1383  std::vector<int32>::const_iterator
1384  dep_iter = dependencies_subset[c].begin(),
1385  dep_end = dependencies_subset[c].end();
1386  for (; dep_iter != dep_end; ++dep_iter) {
1387  int32 d = *dep_iter; // d is cindex_id that c depends on.
1388  if ((*phase_indexes)[d] < 0) // we can't compute c yet because something we depend
1389  break; // on has not yet been computed.
1390  }
1391  if (dep_iter == dep_end) {
1392  // we reached the end and did not break -> all dependencies satisfied
1393  this_phase.push_back(c);
1394  }
1395  }
1396  if (!next_phase_candidates.empty() && this_phase.empty()) {
1397  // this should have been caught earlier so likely a code error rather than
1398  // a problem with user input.
1399  KALDI_ERR << "Your model has a type of recurrence that cannot be computed. "
1400  << "E.g. if x[t] depends on both x[t+1] and x[t-1]... no order "
1401  << "of computation will work.";
1402  }
1403  }
1404 }
1405 
1407  const Nnet &nnet,
1408  const ComputationGraph &graph,
1409  std::vector<std::vector<std::vector<int32> > > *phases_per_segment) {
1410  using namespace computation_graph;
1411  int32 num_cindex_ids = graph.cindexes.size();
1412 
1413  std::vector<int32> cindex_id_to_segment_and_epoch;
1414  std::vector<std::vector<std::vector<int32 > > > epochs_per_segment;
1415  std::vector<bool> epoch_is_trivial;
1416  ComputeEpochInfo(nnet, graph, &cindex_id_to_segment_and_epoch,
1417  &epochs_per_segment, &epoch_is_trivial);
1418 
1419  KALDI_ASSERT(SumVectorSizes(epochs_per_segment) == num_cindex_ids);
1420 
1421  // dependencies_subset contains just the subset of dependencies
1422  // of each cindex_id, that have the same epoch index as
1423  // cindex_id itself. This will be used to correctly order
1424  // cindexes within a certain epoch (relevant for things like
1425  // LSTMs).
1426  std::vector<std::vector<int32> > dependencies_subset;
1427  ComputeDependenciesSubset(graph, cindex_id_to_segment_and_epoch,
1428  &dependencies_subset);
1429  // destroy cindex_id_to_segment_and_epoch, it's no longer needed.
1430  { std::vector<int32> temp; temp.swap(cindex_id_to_segment_and_epoch); }
1431 
1432  // depend_on_subset is a subset of the normal "depend_on" list (i.e. a list of
1433  // all cindex_ids that depend on the current cindex_id), limited to just those
1434  // cindex_ids that have the same epoch index.
1435  std::vector<std::vector<int32> > depend_on_subset;
1436  ComputeGraphTranspose(dependencies_subset, &depend_on_subset);
1437 
1438  int32 num_epoch_indexes = epoch_is_trivial.size(),
1439  num_segments = graph.segment_ends.size();
1440 
1441  // "phase_indexes" is used inside ComputeComputationPhasesForEpoch.
1442  std::vector<int32> phase_indexes(num_cindex_ids, -1);
1443 
1444  phases_per_segment->clear();
1445  phases_per_segment->resize(num_segments);
1446 
1447  for (int32 segment = 0; segment < num_segments; segment++) {
1448  phases_per_segment->reserve(50); // minimize unnecessary copies. 50 is
1449  // very arbitrarily chosen.
1450  for (int32 epoch = 0; epoch < num_epoch_indexes; epoch++)
1452  epochs_per_segment[segment][epoch],
1453  dependencies_subset,
1454  depend_on_subset,
1455  epoch_is_trivial[epoch],
1456  &phase_indexes,
1457  &((*phases_per_segment)[segment]));
1458  }
1459 
1460 
1461  // make sure everything was computable. If the next assert fails it's likely
1462  // a bug in this function or in PruneComputataionGraph.
1463  KALDI_ASSERT(SumVectorSizes(*phases_per_segment) == num_cindex_ids);
1464 }
1465 
1467  graph_(graph), info_(NULL) { }
1468 
1470  const std::vector<ComputationGraphBuilder::CindexInfo> &info,
1471  bool treat_unknown_as_computable):
1472  graph_(graph), info_(&info),
1473  treat_unknown_as_computable_(treat_unknown_as_computable) { }
1474 
1475 
1476 bool CindexSet::operator () (const Cindex &cindex) const {
1477  int32 cindex_id = graph_.GetCindexId(cindex);
1478  if (cindex_id == -1) {
1479  return false;
1480  } else {
1481  if (info_ == NULL) {
1482  return true;
1483  } else {
1485  c = (*info_)[cindex_id].computable;
1486  return (c == ComputationGraphBuilder::kComputable ||
1489  }
1490  }
1491 }
1492 
1494  const std::vector<ComputationGraphBuilder::CindexInfo> &info,
1495  int32 node_id,
1496  bool treat_unknown_as_computable):
1497  graph_(graph), info_(info), node_id_(node_id),
1498  treat_unknown_as_computable_(treat_unknown_as_computable) { }
1499 
1500 bool IndexSet::operator () (const Index &index) const {
1501  int32 cindex_id = graph_.GetCindexId(Cindex(node_id_, index));
1502  if (cindex_id == -1) {
1503  return false;
1504  } else {
1505  ComputationGraphBuilder::ComputableInfo c = info_[cindex_id].computable;
1507  return (c == ComputationGraphBuilder::kComputable ||
1509  else
1511  }
1512 }
1513 
1514 
1516  const Nnet &nnet,
1517  ComputationGraph *graph,
1518  std::vector<std::vector<int32> > *steps,
1519  std::vector<std::pair<int32, int32> > *locations):
1520  nnet_(nnet), graph_(graph), steps_(steps), locations_(locations) {
1521  steps_->clear();
1522  locations_->clear();
1523  int32 num_cindexes = graph_->cindexes.size();
1524  // leave a little space in case a few cindexes are added (unlikely
1525  // but could happen with dim-range nodes).
1526  locations_->reserve(num_cindexes + num_cindexes / 10);
1527  locations_->resize(num_cindexes, std::pair<int32,int32>(-1, -1));
1528 }
1529 
1531  const ComputationRequest &request,
1532  const std::vector<std::vector<int32> > &phases) {
1533  int32 this_num_phases = phases.size();
1534  for (int32 i = 0; i < this_num_phases; i++) {
1535  std::vector<std::vector<Cindex> > sub_phases;
1536  SplitIntoSubPhases(phases[i], &sub_phases);
1537  for (size_t j = 0; j < sub_phases.size(); j++) {
1538  ProcessSubPhase(request, sub_phases[j]);
1539  }
1540  }
1541 }
1542 
1544  const ComputationRequest &request,
1545  bool is_output,
1546  const std::vector<Cindex> &sub_phase) {
1547  int32 io_node = sub_phase[0].first;
1548  if (is_output){
1549  KALDI_ASSERT(nnet_.IsOutputNode(io_node));
1550  } else {
1551  KALDI_ASSERT(nnet_.IsInputNode(io_node));
1552  }
1553  std::string node_name = nnet_.GetNodeName(io_node);
1554  const std::vector<IoSpecification> &inputs_or_outputs =
1555  (is_output ? request.outputs : request.inputs);
1556  int32 io_index = -1;
1557  for (size_t i = 0; i < inputs_or_outputs.size(); i++)
1558  if (inputs_or_outputs[i].name == node_name)
1559  io_index = i;
1560  KALDI_ASSERT(io_index >= 0);
1561  const std::vector<Index> &io_indexes = inputs_or_outputs[io_index].indexes;
1562  std::vector<Cindex> io_cindexes(io_indexes.size());
1563  for (size_t i = 0, size = io_cindexes.size(); i < size; i++) {
1564  io_cindexes[i].first = io_node;
1565  io_cindexes[i].second = io_indexes[i];
1566  }
1567  KALDI_ASSERT(io_cindexes.size() == sub_phase.size());
1568  // we expect the list of cindexes in 'io_cindexes' to be identical to
1569  // that in 'sub_phase' (but they don't have to be in the same order)... for now we check the size, we'll spot-check
1570  // that they are the same later.
1571  // The actual output in 'steps' must be in the same order as
1572  int32 step_index = AddStep(io_cindexes);
1573  // Now spot-check that the cindexes in 'sub_phase' are the same as those
1574  // we just added. [note: they don't have to be in the same order, but
1575  // they should be the same set.]
1576  for (size_t i = 0; i < sub_phase.size(); i += 10) {
1577  const Cindex &cindex = sub_phase[i];
1578  int32 cindex_id = graph_->GetCindexId(cindex);
1579  KALDI_ASSERT(cindex_id >= 0 && (*locations_)[cindex_id].first == step_index);
1580  }
1581 }
1582 
1583 int32 ComputationStepsComputer::AddStep(const std::vector<Cindex> &cindexes,
1584  bool add_if_absent) {
1585  // note: we can't assert that cindexes is nonempty, because it's possible for
1586  // input steps for GeneralComponents to be empty if they require no input
1587  // indexes; and because the compiler code expects component steps to be
1588  // preceded by component-input steps, we can't just omit these empty steps.
1589  // [note: a component-input step is about preparing the input for a component's
1590  // propagation.]
1591  int32 step_index = steps_->size();
1592  steps_->push_back(std::vector<int32>());
1593  std::vector<int32> &step = steps_->back(); // vector of cindex_id.
1594  step.resize(cindexes.size());
1595  size_t row_index = 0;
1596  std::vector<Cindex>::const_iterator iter = cindexes.begin(),
1597  end = cindexes.end();
1598  std::vector<int32>::iterator out_iter = step.begin();
1599  std::pair<int32, int32> *locations = &((*locations_)[0]);
1600  if (!add_if_absent) {
1601  // this version of GetCindexId will not add CindexIds, and
1602  // will crash if CindexIds not present in the graph are
1603  // encountered.
1604  for (; iter != end; ++iter, ++out_iter, ++row_index) {
1605  int32 cindex_id = graph_->GetCindexId(*iter);
1606  *out_iter = cindex_id;
1607  locations[cindex_id].first = step_index;
1608  locations[cindex_id].second = row_index;
1609  }
1610  } else {
1611  for (; iter != end; ++iter, ++out_iter, ++row_index) {
1612  bool is_input = false; // only relevant if we have to add the cindex to
1613  // the computation graph, which we won't for
1614  // inputs (we only might for dim-range nodes
1615  // and for the component-input and component
1616  // steps of non-simple Components.
1617  bool added;
1618  int32 cindex_id = graph_->GetCindexId(*iter, is_input, &added);
1619  *out_iter = cindex_id;
1620  if (added) {
1621  KALDI_ASSERT(cindex_id == static_cast<int32>(locations_->size()));
1622  locations_->resize(cindex_id + 1, std::pair<int32, int32>(-1, -1));
1623  locations_->back().first = step_index;
1624  locations_->back().second = row_index;
1625  locations = &((*locations_)[0]); // in case it was reallocated
1626  } else {
1627  locations[cindex_id].first = step_index;
1628  locations[cindex_id].second = row_index;
1629  }
1630  }
1631  }
1632  return step_index;
1633 }
1634 
1635 
1636 int32 ComputationStepsComputer::AddStep(std::vector<int32> *cindex_ids) {
1637  int32 step_index = steps_->size();
1638  steps_->push_back(std::vector<int32>());
1639  steps_->back().swap(*cindex_ids);
1640  std::vector<int32>::const_iterator iter = steps_->back().begin(),
1641  end = steps_->back().end();
1642  int32 row_index = 0;
1643  std::pair<int32,int32> *locations = &((*locations_)[0]);
1644  size_t num_cindexes = graph_->cindexes.size();
1645  for (; iter != end; ++iter, ++row_index) {
1646  int32 cindex_id = *iter;
1647  KALDI_ASSERT(static_cast<size_t>(cindex_id) < num_cindexes);
1648  locations[cindex_id].first = step_index;
1649  locations[cindex_id].second = row_index;
1650  }
1651  return step_index;
1652 }
1653 
1654 
1656  const std::vector<int32> &cindex_ids,
1657  std::vector<Cindex> *cindexes) const {
1658  cindexes->resize(cindex_ids.size());
1659  size_t num_cindexes = graph_->cindexes.size();
1660  std::vector<int32>::const_iterator iter = cindex_ids.begin(),
1661  end = cindex_ids.end();
1662  std::vector<Cindex>::iterator out_iter = cindexes->begin();
1663  for (; iter != end; ++iter, ++out_iter) {
1664  int32 cindex_id = *iter;
1665  KALDI_ASSERT(static_cast<size_t>(cindex_id) < num_cindexes);
1666  *out_iter = graph_->cindexes[cindex_id];
1667  }
1668 }
1669 
1670 
1672  const std::vector<Cindex> &cindexes,
1673  std::vector<int32> *cindex_ids) const {
1674  cindex_ids->resize(cindexes.size());
1675  std::vector<Cindex>::const_iterator iter = cindexes.begin(),
1676  end = cindexes.end();
1677  std::vector<int32>::iterator out_iter = cindex_ids->begin();
1678  for (; iter != end; ++iter, ++out_iter) {
1679  int32 cindex_id = graph_->GetCindexId(*iter);
1680  KALDI_ASSERT(cindex_id >= 0);
1681  *out_iter = cindex_id;
1682  }
1683 }
1684 
1685 
1686 // static
1688  const std::vector<Cindex> &cindexes,
1689  std::vector<Index> *indexes) {
1690  indexes->resize(cindexes.size());
1691  std::vector<Cindex>::const_iterator iter = cindexes.begin(),
1692  end = cindexes.end();
1693  std::vector<Index>::iterator out_iter = indexes->begin();
1694  for (; iter != end; ++iter, ++out_iter)
1695  *out_iter = iter->second;
1696 }
1697 
1698 // static
1700  const std::vector<Index> &indexes,
1701  int32 node_index,
1702  std::vector<Cindex> *cindexes) {
1703  KALDI_ASSERT(node_index >= 0);
1704  cindexes->resize(indexes.size());
1705  std::vector<Index>::const_iterator iter = indexes.begin(),
1706  end = indexes.end();
1707  std::vector<Cindex>::iterator out_iter = cindexes->begin();
1708  for (; iter != end; ++iter, ++out_iter) {
1709  out_iter->first = node_index;
1710  out_iter->second = *iter;
1711  }
1712 }
1713 
1714 
1715 
1716 
1718  const std::vector<Cindex> &step) {
1719  KALDI_ASSERT(!step.empty());
1720  int32 component_node_index = step.front().first;
1721  int32 component_input_index = component_node_index - 1;
1722  KALDI_ASSERT(nnet_.IsComponentNode(component_node_index));
1723  const NetworkNode &node = nnet_.GetNode(component_node_index);
1724  int32 c = node.u.component_index;
1725  const Component *component = nnet_.GetComponent(c);
1726  if (component->Properties() & kSimpleComponent) {
1727  // for simple components, the input cindexes will be the same as the
1728  // output ones except for the node index, so we do a shortcut that's
1729  // faster (no following dependencies).
1730  std::vector<Cindex> input_step(step.size());
1731  input_step.resize(step.size());
1732  std::vector<Cindex>::iterator iter = input_step.begin(),
1733  end = input_step.end();
1734  std::vector<Cindex>::const_iterator src = step.begin();
1735  for (; iter != end; ++iter,++src) {
1736  iter->first = component_input_index;
1737  iter->second = src->second;
1738  }
1739  AddStep(input_step);
1740  AddStep(step);
1741  } else {
1742  std::vector<int32> step_cindex_ids;
1743  ConvertToCindexIds(step, &step_cindex_ids);
1744  // to get the input cindexes we need to follow dependencies back.
1745  unordered_set<int32> input_cindex_ids;
1746  std::vector<int32>::iterator iter = step_cindex_ids.begin(),
1747  end = step_cindex_ids.end();
1748  for (; iter != end; ++iter) {
1749  int32 c = *iter;
1750  const std::vector<int32> &dependencies = graph_->dependencies[c];
1751  std::vector<int32>::const_iterator dep_iter = dependencies.begin(),
1752  dep_end = dependencies.end();
1753  for (; dep_iter != dep_end; ++dep_iter) {
1754  int32 d = *dep_iter;
1755  input_cindex_ids.insert(d);
1756  }
1757  }
1758  // Convert to Cindexes so we can sort them as Cindexes.
1759  std::vector<Cindex> input_step;
1760  input_step.reserve(input_cindex_ids.size());
1761  unordered_set<int32>::iterator set_iter = input_cindex_ids.begin(),
1762  set_end = input_cindex_ids.end();
1763  for (; set_iter != set_end; ++set_iter) {
1764  int32 c = *set_iter;
1765  input_step.push_back(graph_->cindexes[c]);
1766  }
1767 
1768  // sort the input cindexes.
1769  std::sort(input_step.begin(), input_step.end());
1770 
1771  if (component->Properties() & kReordersIndexes) {
1772  std::vector<Index> indexes, input_indexes;
1773  ConvertToIndexes(input_step, &input_indexes);
1774  ConvertToIndexes(step, &indexes);
1775 
1776 
1777  size_t orig_size = indexes.size() + input_indexes.size();
1778 
1779  // the component wants to have the opportunity to change the
1780  // order of these indexes from their default.
1781  component->ReorderIndexes(&input_indexes, &indexes);
1782 
1783  bool added_padding = (orig_size != indexes.size() + input_indexes.size());
1784 
1785  // Now convert back from indexes to cindexes (we know the
1786  // node-index in each case)
1787  std::vector<Cindex> reordered_step;
1788  ConvertToCindexes(indexes, component_node_index, &reordered_step);
1789  ConvertToCindexes(input_indexes, component_input_index, &input_step);
1790  // the 'added_padding' argument becomes the 'add_if_absent' arg of
1791  // AddStep, so it knows to expect that it might have to add new CindexIds.
1792  AddStep(input_step, added_padding);
1793  AddStep(reordered_step, added_padding);
1794  } else {
1795  AddStep(input_step);
1796  // it's more efficient to add the step with cindex_ids; and we have these
1797  // available, so we do it that way. (in the other branch where
1798  // the flag kReordersIndexes was present, we couldn't do this because
1799  // of the reordering).
1800  AddStep(&step_cindex_ids);
1801  }
1802  }
1803 }
1804 
1805 
1807  const std::vector<int32> &cindex_ids,
1808  std::vector<std::pair<int32, int32> > *locations) const {
1809  locations->resize(cindex_ids.size());
1810  std::vector<int32>::const_iterator iter = cindex_ids.begin(),
1811  end = cindex_ids.end();
1812  std::vector<std::pair<int32, int32> >::iterator out_iter =
1813  locations->begin();
1814  // note, locations_ and locations are different variables.
1815  std::pair<int32, int32> *locations_ptr = &((*locations_)[0]);
1816  size_t num_cindexes = locations_->size();
1817  for (; iter != end; ++iter, ++out_iter) {
1818  int32 cindex_id = *iter;
1819  KALDI_ASSERT(static_cast<size_t>(cindex_id) < num_cindexes);
1820  int32 step = locations_ptr[cindex_id].first,
1821  row = locations_ptr[cindex_id].second;
1822  KALDI_ASSERT(step >= 0);
1823  out_iter->first = step;
1824  out_iter->second = row;
1825  }
1826 }
1827 
1829  const std::vector<Cindex> &sub_phase) {
1830  int32 dim_range_node = sub_phase[0].first;
1831  KALDI_ASSERT(nnet_.IsDimRangeNode(dim_range_node));
1832  const NetworkNode &node = nnet_.GetNode(dim_range_node);
1833  // 'input_node_index' is the node index of the component or input node
1834  // that this dim-range node gets its input from.
1835  int32 input_node_index = node.u.node_index;
1836  // input_cindexes will give us the cindexes of the component or input node
1837  // that is the input to this dim-range node
1838  std::vector<Cindex> input_cindexes(sub_phase);
1839  for (std::vector<Cindex>::iterator iter = input_cindexes.begin(),
1840  end = input_cindexes.end(); iter != end; ++iter)
1841  iter->first = input_node_index;
1842  std::vector<int32> input_cindex_ids;
1843  ConvertToCindexIds(input_cindexes, &input_cindex_ids);
1844  std::vector<std::pair<int32, int32> > locations;
1845  ConvertToLocations(input_cindex_ids, &locations);
1846 
1847  // get a list of the source step indexes (corresponding to computations for the
1848  // source component-node)
1849  std::unordered_set<int32> source_step_indexes;
1850  KALDI_ASSERT(!locations.empty());
1851  std::vector<std::pair<int32, int32> >::const_iterator
1852  locations_iter = locations.begin(),
1853  locations_end = locations.end();
1854 
1855  // 'cur_source_step_index' is just an optimization to prevent unnecessary
1856  // unordered_set inserts.
1857  int32 cur_source_step_index = -1;
1858  for (; locations_iter != locations_end; ++locations_iter) {
1859  int32 source_step_index = locations_iter->first;
1860  if (source_step_index != cur_source_step_index) {
1861  cur_source_step_index = source_step_index;
1862  source_step_indexes.insert(cur_source_step_index);
1863  }
1864  }
1865 
1866  std::unordered_set<int32>::const_iterator
1867  source_step_iter = source_step_indexes.begin(),
1868  source_step_end = source_step_indexes.end();
1869  // iterating over the indexes of the source steps.
1870  for (; source_step_iter != source_step_end; ++source_step_iter) {
1871  int32 source_step_index = *source_step_iter;
1872  std::pair<int32, int32> p(source_step_index, dim_range_node);
1873  if (dim_range_nodes_.count(p) > 0) {
1874  // We don't need to do anything; a dim-range node already exists for this
1875  // step and this node index.
1876  continue;
1877  }
1878  dim_range_nodes_.insert(p);
1879  const std::vector<int32> &source_step = (*steps_)[source_step_index];
1880  // 'cindexes' will be the cindexes of the new step that we're going to add.
1881  std::vector<Cindex> cindexes;
1882  ConvertToCindexes(source_step, &cindexes);
1883  std::vector<Cindex>::iterator iter = cindexes.begin(),
1884  end = cindexes.end();
1885  for (; iter != end; ++iter)
1886  iter->first = dim_range_node;
1887  bool add_if_absent = true;
1888  // this add_if_absent says, even if cindexes were not in the graph,
1889  // add them. This is possible; the step will contain all cindexes for the
1890  // input step, even if they won't be needed. (This is costless; it's just
1891  // setting up a sub-matrix).
1892  AddStep(cindexes, add_if_absent);
1893  }
1894 }
1895 
1897  const ComputationRequest &request,
1898  const std::vector<Cindex> &sub_phase) {
1899  KALDI_ASSERT(!sub_phase.empty());
1900  int32 node_index = sub_phase[0].first;
1901  KALDI_ASSERT(sub_phase.back().first == node_index);
1902  if (nnet_.IsComponentNode(node_index)) {
1903  ProcessComponentStep(sub_phase);
1904  } else if (nnet_.IsInputNode(node_index)) {
1905  ProcessInputOrOutputStep(request, false, sub_phase);
1906  } else if (nnet_.IsOutputNode(node_index)) {
1907  ProcessInputOrOutputStep(request, true, sub_phase);
1908  } else if (nnet_.IsDimRangeNode(node_index)) {
1909  // this might turn out to be multiple steps, see the code.
1910  ProcessDimRangeSubPhase(sub_phase);
1911  } else if (nnet_.IsComponentInputNode(node_index)) {
1912  // We actually do nothing with these sub-phases, because they are processed
1913  // when we process the associated component's sub-phase/step. Doing it this
1914  // way resolves certain problems.
1915  return;
1916  } else {
1917  KALDI_ERR << "Unknown node type.";
1918  }
1919 }
1920 
1921 
1923  int32 num_cindexes = graph_->cindexes.size();
1924  KALDI_ASSERT(locations_->size() == num_cindexes);
1925  for (int32 c = 0; c < num_cindexes; c++) {
1926  int32 step = (*locations_)[c].first,
1927  row = (*locations_)[c].second;
1928  if (!(step >= 0 && row >= 0 && (*steps_)[step][row] == c)) {
1929  // normally the 'locations' of cindexes should be unique, so we should
1930  // never normally reach this point; but it's not an error to have
1931  // duplicates of the cindexes used for 'padding' by the ReorderIndexes()
1932  // function of non-simple Components. So we check whether that's the case
1933  // before we die.
1934  if (graph_->cindexes[c].second.t != kNoTime) {
1935  // if this happens it will likely require some debugging by Dan.
1936  KALDI_ERR << "Error in computing computation steps (likely code error)";
1937  }
1938  }
1939 
1940  }
1941 }
1942 
1944  const std::vector<int32> &phase,
1945  std::vector<std::vector<Cindex> > *sub_phases) const {
1946  std::vector<Cindex> phase_cindexes;
1947  ConvertToCindexes(phase, &phase_cindexes);
1948  KALDI_ASSERT(!phase_cindexes.empty());
1949  std::sort(phase_cindexes.begin(), phase_cindexes.end());
1950  // 'sub_phase_begins' is the indexes onto 'phase_cindees' that
1951  // start a run of the same node-index
1952  std::vector<size_t> segment_begins;
1953  int32 cur_node_index = -1;
1954  size_t size = phase_cindexes.size();
1955  for (size_t i = 0; i < size; i++) {
1956  if (phase_cindexes[i].first != cur_node_index) {
1957  cur_node_index = phase_cindexes[i].first;
1958  segment_begins.push_back(i);
1959  }
1960  }
1961  size_t num_sub_phases = segment_begins.size();
1962  segment_begins.push_back(size);
1963  sub_phases->clear();
1964  sub_phases->resize(num_sub_phases);
1965  for (size_t i = 0; i < num_sub_phases; i++) {
1966  size_t this_begin = segment_begins[i],
1967  this_end = segment_begins[i+1];
1968  (*sub_phases)[i].insert((*sub_phases)[i].end(),
1969  phase_cindexes.begin() + this_begin,
1970  phase_cindexes.begin() + this_end);
1971  }
1972 }
1973 
1974 
1975 
1976 } // namespace nnet3
1977 } // namespace kaldi
virtual bool IsComputable(const MiscComputationInfo &misc_info, const Index &output_index, const IndexSet &input_index_set, std::vector< Index > *used_inputs) const
This function only does something interesting for non-simple Components, and it exists to make it pos...
void ConvertToLocations(const std::vector< int32 > &cindex_ids, std::vector< std::pair< int32, int32 > > *locations) const
void GetDependencies(const Index &index, std::vector< Cindex > *used_inputs) const
This function exists to enable us to manage optional dependencies, i.e.
This code computes Goodness of Pronunciation (GOP) and extracts phone-level pronunciation feature for...
Definition: chain.dox:20
int32 NumNodes() const
Definition: nnet-nnet.h:126
ComputationStepsComputer(const Nnet &nnet, ComputationGraph *graph, std::vector< std::vector< int32 > > *steps, std::vector< std::pair< int32, int32 > > *locations)
Constructor.
void ExplainWhyNotComputable(int32 cindex_id) const
static void ComputeEpochInfo(const Nnet &nnet, const ComputationGraph &graph, std::vector< int32 > *cindex_id_to_segment_and_epoch, std::vector< std::vector< std::vector< int32 > > > *epochs_per_segment, std::vector< bool > *epoch_is_trivial)
This function computes certain information about "epochs" of cindex_ids.
const std::string & GetNodeName(int32 node_index) const
returns individual node name.
Definition: nnet-nnet.cc:684
MiscComputationInfo misc_info
misc_info is for extensibility to things that don&#39;t easily fit into the framework.
Abstract base-class for neural-net components.
void PrintCindex(std::ostream &os, const Cindex &cindex, const std::vector< std::string > &node_names)
Definition: nnet-common.cc:432
int32 GetVerboseLevel()
Get verbosity level, usually set via command line &#39;–verbose=&#39; switch.
Definition: kaldi-error.h:60
void GetComputableInfo(std::vector< std::vector< bool > > *computable) const
An abstract representation of a set of Indexes.
void ConvertToCindexIds(const std::vector< Cindex > &cindexes, std::vector< int32 > *cindex_ids) const
void PrintCindexId(std::ostream &os, int32 cindex_id) const
std::vector< std::pair< int32, int32 > > * locations_
locations_ is a map from cindex_id to the pair of indexes into steps_ where that cindex_id resides...
virtual void GetInputIndexes(const MiscComputationInfo &misc_info, const Index &output_index, std::vector< Index > *desired_indexes) const
This function only does something interesting for non-simple Components.
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 Check(int32 start_cindex_id) const
const std::vector< ComputationGraphBuilder::CindexInfo > * info_
kaldi::int32 int32
std::ostream & operator<<(std::ostream &ostream, const Index &index)
Definition: nnet-common.cc:424
void ProcessDimRangeSubPhase(const std::vector< Cindex > &sub_phase)
std::vector< IoSpecification > inputs
void SplitIntoSubPhases(const std::vector< int32 > &phase, std::vector< std::vector< Cindex > > *sub_phase) const
bool operator()(const Cindex &cindex) const
Parenthesis operator; returns true if this cindex exists in the set.
int32 RoundUpToNearestPowerOfTwo(int32 n)
Definition: kaldi-math.cc:32
bool IsComponentNode(int32 node) const
Returns true if this is a component node, meaning that it is of type kComponent.
Definition: nnet-nnet.cc:132
void SortAndUniq(std::vector< T > *vec)
Sorts and uniq&#39;s (removes duplicates) from a vector.
Definition: stl-utils.h:39
static void ConvertToIndexes(const std::vector< Cindex > &cindexes, std::vector< Index > *indexes)
void AddInputToGraph(const ComputationRequest &request, const Nnet &nnet, ComputationGraph *graph)
void ComputeComputationGraph(const Nnet &nnet, const ComputationRequest &request, ComputationGraph *graph)
struct Index is intended to represent the various indexes by which we number the rows of the matrices...
Definition: nnet-common.h:44
void ComputeGraphTranspose(const std::vector< std::vector< int32 > > &graph, std::vector< std::vector< int32 > > *graph_transpose)
Outputs a graph in which the order of arcs is reversed.
Definition: nnet-graph.cc:63
void ComputeRequiredArray(int32 start_cindex_id, std::vector< bool > *required) const
bool operator()(const Index &index) const
Returns true if this Index exists in the set.
const ComputationGraph & graph_
std::pair< int32, Index > Cindex
Definition: nnet-common.h:115
void AddOutputToGraph(const ComputationRequest &request, const Nnet &nnet, ComputationGraph *graph)
const size_t count
std::vector< Cindex > cindexes
The mapping of cindex_id to Cindex.
const NetworkNode & GetNode(int32 node) const
returns const reference to a particular numbered network node.
Definition: nnet-nnet.h:146
void ConvertToCindexes(const std::vector< int32 > &cindex_ids, std::vector< Cindex > *cindexes) const
static int32 SumVectorSizes(const std::vector< std::vector< int32 > > &vec)
std::vector< std::vector< int32 > > dependencies
dependencies[cindex_id] gives you the list of other cindex_ids that this particular cindex_id directl...
int32 GetCindexId(const Cindex &cindex, bool is_input, bool *is_new)
Maps a Cindex to an integer cindex_id.
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
void Check() const
This is only to be called after you have called ComputeForSegment for all the segments.
unordered_map< Cindex, int32, CindexHasher > cindex_to_cindex_id_
Maps each Cindex to an integer cindex_id: reverse mapping of "cindexes".
void ComputeComputationPhases(const Nnet &nnet, const ComputationGraph &graph, std::vector< std::vector< std::vector< int32 > > > *phases_per_segment)
This function divides a computation into &#39;phases&#39;, where a &#39;phase&#39; is a collection of cindexes which ...
virtual int32 Properties() const =0
Return bitmask of the component&#39;s properties.
virtual void ReorderIndexes(std::vector< Index > *input_indexes, std::vector< Index > *output_indexes) const
This function only does something interesting for non-simple Components.
IndexSet(const ComputationGraph &graph, const std::vector< ComputationGraphBuilder::CindexInfo > &info, int32 node_id, bool treat_unknown_as_computable)
This constructor creates the set of all Indexes x such that a Cindex (node_id, x) which is computable...
std::vector< bool > is_input
For each Cindex this tells us whether it was provided as an input to the network. ...
std::vector< std::vector< int32 > > depend_on_this_
std::vector< int32 > segment_ends
This variable is only of particular interest in a &#39;multi-segment&#39; computation, which is used while cr...
struct rnnlm::@11::@12 n
void ComputeNnetComputationEpochs(const Nnet &nnet, std::vector< int32 > *node_to_epoch)
This function computes the order in which we need to compute each node in the graph, where each node-index n maps to an epoch-index t = 0, 1, ...
Definition: nnet-graph.cc:265
std::vector< std::vector< int32 > > * steps_
steps_ is a pointer to an output that&#39;s passed in in the constructor.
#define KALDI_ERR
Definition: kaldi-error.h:147
#define KALDI_PARANOID_ASSERT(cond)
Definition: kaldi-error.h:206
void Compute(const ComputationRequest &request)
void PrintIntegerVector(std::ostream &os, const std::vector< int32 > &ints)
Definition: nnet-common.cc:525
void Renumber(int32 start_cindex_id, const std::vector< bool > &keep)
This function renumbers the cindex-ids (but only those with index c >= start_cindex_id,.
bool IsComputable(const Index &ind, const CindexSet &cindex_set, std::vector< Cindex > *used_inputs) const
Has the same purpose and interface as the IsComputable function of the SumDescriptor function...
NetworkNode is used to represent, three types of thing: either an input of the network (which pretty ...
Definition: nnet-nnet.h:81
Component * GetComponent(int32 c)
Return component indexed c. Not a copy; not owned by caller.
Definition: nnet-nnet.cc:150
This file contains a few functions that treat the neural net as a graph on nodes: e...
void ProcessComponentStep(const std::vector< Cindex > &step)
void ComputeForSegment(const ComputationRequest &request, const std::vector< std::vector< int32 > > &phases)
You call this once for each segment, in order (note: for normal, non-online computations, there is only one segment).
std::unordered_set< std::pair< int32, int32 >, PairHasher< int32 > > dim_range_nodes_
dim_range_nodes_ is used when allocating steps for nodes of type kDimRangeNode.
std::vector< Index > indexes
ComputableInfo ComputeComputableInfo(int32 cindex_id) const
static void ComputeComputationPhasesForEpoch(const Nnet &nnet, const ComputationGraph &graph, const std::vector< int32 > &this_epoch, const std::vector< std::vector< int32 > > &dependencies_subset, const std::vector< std::vector< int32 > > &depend_on_subset, bool epoch_is_trivial, std::vector< int32 > *phase_indexes, std::vector< std::vector< int32 > > *phases)
const std::vector< ComputationGraphBuilder::CindexInfo > & info_
#define KALDI_ASSERT(cond)
Definition: kaldi-error.h:185
std::vector< IoSpecification > outputs
void ProcessInputOrOutputStep(const ComputationRequest &request, bool is_output, const std::vector< Cindex > &sub_phase)
const ComputationGraph & graph_
ComputationGraphBuilder(const Nnet &nnet, ComputationGraph *graph)
#define KALDI_VLOG(v)
Definition: kaldi-error.h:156
union kaldi::nnet3::NetworkNode::@15 u
void Print(std::ostream &os, const std::vector< std::string > &node_names)
This function, useful for debugging/visualization purposes, prints out a summary of the computation g...
int32 GetNodeIndex(const std::string &node_name) const
returns index associated with this node name, or -1 if no such index.
Definition: nnet-nnet.cc:466
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
#define KALDI_LOG
Definition: kaldi-error.h:153
int32 AddStep(const std::vector< Cindex > &cindexes, bool add_if_absent=false)
static void ComputeDependenciesSubset(const ComputationGraph &graph, const std::vector< int32 > &cindex_id_to_segment_and_epoch, std::vector< std::vector< int32 > > *dependencies_subset)
This function outputs to dependencies_subset[c], for each cindex_id c, the subset of elements d of gr...
bool IsDimRangeNode(int32 node) const
Returns true if this is a dim-range node, meaning that it is of type kDimRange.
Definition: nnet-nnet.cc:138
The first step in compilation is to turn the ComputationSpecification into a ComputationGraph, where for each Cindex we have a list of other Cindexes that it depends on.
const int kNoTime
Definition: nnet-common.cc:573
CindexSet(const ComputationGraph &graph)
with this constructor, represents the set of all Cindexes that exist in the graph.
int32 RandInt(int32 min_val, int32 max_val, struct RandomState *state)
Definition: kaldi-math.cc:95
bool IsComponentInputNode(int32 node) const
Returns true if this is component-input node, i.e.
Definition: nnet-nnet.cc:172
void ProcessSubPhase(const ComputationRequest &request, const std::vector< Cindex > &sub_phase)