A while loop with a single loop variable looks like this: (output) ^ +---------------+ | | body subgraph +-------------+ Exit +---------------+ | ^ ^ | | | | Switch<--------+ v ^ | NextIteration | +------+--------+ | +---->| cond subgraph | | | +----------
| 170 | // duplicated for each loop variable. |
| 171 | // TODO(skyewm): link to public version of design doc |
| 172 | Status BuildWhileLoop(const Scope& scope, const std::vector<Output>& inputs, |
| 173 | const CondGraphBuilderFn& cond, |
| 174 | const BodyGraphBuilderFn& body, const string& frame_name, |
| 175 | OutputList* outputs, bool create_while_ctx, |
| 176 | Output* cond_output) { |
| 177 | DCHECK(!inputs.empty()); |
| 178 | DCHECK(outputs != nullptr); |
| 179 | DCHECK(outputs->empty()); |
| 180 | |
| 181 | TF_RETURN_IF_ERROR(scope.status()); |
| 182 | const size_t num_loop_vars = inputs.size(); |
| 183 | |
| 184 | std::vector<Output> enter_outputs(num_loop_vars); |
| 185 | for (int i = 0; i < num_loop_vars; ++i) { |
| 186 | enter_outputs[i] = internal::Enter(scope, inputs[i], frame_name); |
| 187 | } |
| 188 | TF_RETURN_IF_ERROR(scope.status()); |
| 189 | |
| 190 | std::vector<Output> merge_outputs(num_loop_vars); |
| 191 | for (int i = 0; i < num_loop_vars; ++i) { |
| 192 | TF_RETURN_IF_ERROR( |
| 193 | CreateMerge(scope, i, enter_outputs[i], &merge_outputs[i])); |
| 194 | } |
| 195 | |
| 196 | Output cond_out; |
| 197 | TF_RETURN_IF_ERROR(CreateCond(scope, cond, merge_outputs, &cond_out)); |
| 198 | if (cond_output != nullptr) *cond_output = cond_out; |
| 199 | |
| 200 | std::vector<Output> switch_trues(num_loop_vars); |
| 201 | std::vector<Output> switch_falses(num_loop_vars); |
| 202 | for (int i = 0; i < num_loop_vars; ++i) { |
| 203 | auto switch_i = Switch(scope, merge_outputs[i], cond_out); |
| 204 | switch_trues[i] = switch_i.output_true; |
| 205 | switch_falses[i] = switch_i.output_false; |
| 206 | } |
| 207 | TF_RETURN_IF_ERROR(scope.status()); |
| 208 | |
| 209 | std::vector<Output> body_outputs; |
| 210 | TF_RETURN_IF_ERROR(CreateBody(scope, body, switch_trues, &body_outputs)); |
| 211 | |
| 212 | std::vector<Output> next_outputs(num_loop_vars); |
| 213 | for (int i = 0; i < num_loop_vars; ++i) { |
| 214 | next_outputs[i] = NextIteration(scope, body_outputs[i]); |
| 215 | DCHECK_EQ(next_outputs[i].node()->name(), NextIterationName(scope, i)); |
| 216 | } |
| 217 | TF_RETURN_IF_ERROR(scope.status()); |
| 218 | |
| 219 | // Create the backedges from the NextIteration nodes to the Merge nodes. |
| 220 | for (int i = 0; i < num_loop_vars; ++i) { |
| 221 | const int merge_backedge_output_index = 1; |
| 222 | scope.graph()->AddEdge(next_outputs[i].node(), next_outputs[i].index(), |
| 223 | merge_outputs[i].node(), |
| 224 | merge_backedge_output_index); |
| 225 | } |
| 226 | |
| 227 | outputs->resize(num_loop_vars); |
| 228 | for (int i = 0; i < num_loop_vars; ++i) { |
| 229 | (*outputs)[i] = internal::Exit(scope, switch_falses[i]); |