| 2406 | } |
| 2407 | |
| 2408 | std::unique_ptr<FunctionBody> SymbolicGradientHelper::Compute() { |
| 2409 | FunctionBody* gbody = new FunctionBody; |
| 2410 | Copy(gbody); // copy fbody_ into gbody. |
| 2411 | |
| 2412 | Graph* g = gbody->graph; |
| 2413 | |
| 2414 | const int num_y = static_cast<int>(gbody->ret_nodes.size()); |
| 2415 | |
| 2416 | // Populate 'y_node_outputs_' with node function body outputs. |
| 2417 | // Populate 'y_grad_nodes' with initial gradient nodes for each return node |
| 2418 | // of the original function body (these will be 'arg' nodes in the function |
| 2419 | // gradient body). |
| 2420 | std::vector<NodeOut> y_node_outputs; |
| 2421 | y_node_outputs.reserve(num_y); |
| 2422 | std::vector<NodeOut> y_grad_node_outputs; |
| 2423 | y_grad_node_outputs.reserve(num_y); |
| 2424 | for (int i = 0; i < num_y; ++i) { |
| 2425 | Node* y = gbody->ret_nodes[i]; |
| 2426 | y_node_outputs.push_back({y, 0}); |
| 2427 | DCHECK_EQ(y->type_string(), kRetOp); |
| 2428 | const DataType dtype = y->input_type(0); |
| 2429 | const int index = static_cast<int>(gbody->arg_nodes.size()); |
| 2430 | Node* dy = AddArg(g, dtype, index); |
| 2431 | gbody->arg_types.push_back(dtype); |
| 2432 | gbody->arg_nodes.push_back(dy); |
| 2433 | y_grad_node_outputs.push_back({dy, 0}); |
| 2434 | } |
| 2435 | |
| 2436 | // Populate 'x_nodes' with function args (excluding 'y_grad_node_outputs'). |
| 2437 | const size_t num_x = fbody_->arg_nodes.size(); |
| 2438 | std::vector<NodeOut> x_node_outputs; |
| 2439 | x_node_outputs.reserve(num_x); |
| 2440 | for (size_t i = 0; i < fbody_->arg_nodes.size(); ++i) { |
| 2441 | x_node_outputs.push_back({gbody->arg_nodes[i], 0}); |
| 2442 | } |
| 2443 | |
| 2444 | // Call AddSymbolicGradients which will add nodes to graph 'g' that |
| 2445 | // compute the function gradient (adding an entry in 'x_grad_node_outputs' |
| 2446 | // for each node in 'x_node_outputs'). |
| 2447 | std::vector<NodeOut> x_grad_node_outputs; |
| 2448 | TF_CHECK_OK(AddSymbolicGradients(y_node_outputs, x_node_outputs, |
| 2449 | y_grad_node_outputs, &x_grad_node_outputs, |
| 2450 | g)); |
| 2451 | |
| 2452 | // Remove the old return nodes from the function body. |
| 2453 | for (Node* n : gbody->ret_nodes) { |
| 2454 | g->RemoveNode(n); |
| 2455 | } |
| 2456 | gbody->ret_types = fbody_->arg_types; |
| 2457 | // TODO(apassos): use the right dtype for gradients of resource variables |
| 2458 | for (int i = 0; i < gbody->ret_types.size(); ++i) { |
| 2459 | if (gbody->ret_types[i] == DT_RESOURCE) { |
| 2460 | gbody->ret_types[i] = DT_FLOAT; |
| 2461 | } |
| 2462 | } |
| 2463 | gbody->ret_nodes.clear(); |
| 2464 | // Add new return nodes to the function gradient body for each node |
| 2465 | // in 'x_grad_nodes'. |
no test coverage detected