in tensorflow_fold/loom/weaver.cc [716:797]
void Weaver::Finalize() {
// Make sure Finalize only does anything the first time.
if (finalized_) return;
finalized_ = true;
if (max_depth_ == -1) {
max_depth_ = Deepest();
}
// Add PassThroughs so that all the outputs are at the 'max_depth_'.
std::vector<tensor_idx_t> deepened_outputs;
for (tensor_idx_t output_result_id : output_result_ids_) {
deepened_outputs.push_back(Deepen(output_result_id, max_depth_));
}
// Given a depth, an op_idx and an op_output_idx, wiring_offset tells us where
// in the state Tensor of the appropriate type_shape the 'op_output_idx'th
// output of 'op' will start, after the concat which takes place in sub-layer
// (3) of the previous loom layer.
std::map<std::tuple<tensor_idx_t, tensor_idx_t, tensor_idx_t>,
tensor_idx_t> wiring_offset;
// Named tensors, constants and batch input members have depth=0, op_idx=-1
// and op_output_idx=-1, and we don't have to add anything to their 'pos_idx'
// to get their index in the initial state tensor.
wiring_offset[std::make_tuple(0, -1, -1)] = 0;
for (tensor_idx_t depth = 1; depth <= max_depth_; ++depth) {
std::vector<tensor_idx_t> concat_offset(num_type_shapes_);
for (tensor_idx_t op_idx = 0; op_idx < num_ops_; ++op_idx) {
// Note: the number of times op 'op_idx' got called at 'depth' is the same
// as the number of 0th arguments passed in.
tensor_idx_t num_op_calls = wiring_results_[
std::make_tuple(depth, op_idx, 0)].size();
for (tensor_idx_t output_idx = 0;
output_idx < op_output_ts_idx_[op_idx].size();
++output_idx) {
// The TypeShape for the current output determines which state tensor
// this batch of outputs ended up in.
tensor_idx_t ts_idx = op_output_ts_idx_[op_idx][output_idx];
wiring_offset[std::make_tuple(depth, op_idx, output_idx)] =
concat_offset[ts_idx];
// We're incrementing concat_offset by 'num_op_calls' because the
// current op must have 'num_op_calls' as the batch size for all of its
// outputs (including this one.)
concat_offset[ts_idx] += num_op_calls;
}
}
}
// Populate 'final_wiring_' with the values that will end up in the wiring
// diagram placeholder variables in the loom.
for (tensor_idx_t depth = 1; depth <= max_depth_; ++depth) {
for (tensor_idx_t op_idx = 0; op_idx < num_ops_; ++op_idx) {
for (tensor_idx_t arg_idx = 0; arg_idx < op_input_ts_idx_[op_idx].size();
++arg_idx) {
auto key = std::make_tuple(depth, op_idx, arg_idx);
const std::vector<tensor_idx_t> &my_wiring_results =
wiring_results_[key];
std::vector<tensor_idx_t> &my_final_wiring = final_wiring_[key];
for (tensor_idx_t result_id : my_wiring_results) {
const LoomResult &r = loom_results_[result_id];
my_final_wiring.push_back(
r.pos_idx + wiring_offset[std::make_tuple(
r.depth, r.op_idx, r.op_output_idx)]);
}
}
}
}
// Populate 'final_output_wiring_'
final_output_wiring_.resize(num_type_shapes_);
for (tensor_idx_t deepened_output : deepened_outputs) {
const LoomResult &r = loom_results_[deepened_output];
final_output_wiring_[r.ts_idx].push_back(
r.pos_idx + wiring_offset[std::make_tuple(
r.depth, r.op_idx, r.op_output_idx)]);
}
}