| 247 | } |
| 248 | |
| 249 | Status SymbolicGradientBuilder::Initialize() { |
| 250 | if (outputs_.size() != grad_inputs_.size()) { |
| 251 | return errors::InvalidArgument( |
| 252 | "Must specify a gradient input for each output."); |
| 253 | } |
| 254 | std::vector<bool> reachable_nodes = GetReachableNodes(); |
| 255 | for (const Output& input : inputs_) { |
| 256 | if (!reachable_nodes[input.node()->id()]) { |
| 257 | return errors::InvalidArgument( |
| 258 | "Cannot compute the partial derivative for node '", |
| 259 | input.node()->name(), |
| 260 | "' as it's unreachable from the output node(s)."); |
| 261 | } |
| 262 | } |
| 263 | grad_outputs_->clear(); |
| 264 | grad_outputs_->resize(inputs_.size()); |
| 265 | |
| 266 | std::unordered_set<int> output_nodes; |
| 267 | output_nodes.reserve(outputs_.size()); |
| 268 | for (size_t i = 0; i < outputs_.size(); ++i) { |
| 269 | output_nodes.insert(outputs_[i].node()->id()); |
| 270 | } |
| 271 | |
| 272 | std::unordered_set<int> stop_backprop_nodes = |
| 273 | GetStopBackpropNodes(reachable_nodes, output_nodes); |
| 274 | |
| 275 | // Populate `input_nodes_` from Outputs in `inputs_`. |
| 276 | input_nodes_.reserve(inputs_.size()); |
| 277 | for (size_t i = 0; i < inputs_.size(); ++i) { |
| 278 | input_nodes_.insert({inputs_[i], i}); |
| 279 | } |
| 280 | |
| 281 | // TODO(andydavis) Consider a more efficient data structure for `pending_` to |
| 282 | // handle computing gradients over small subgraphs from a very large graph. |
| 283 | pending_.resize(scope_.graph()->num_node_ids(), 0); |
| 284 | { |
| 285 | backprops_.clear(); |
| 286 | std::unordered_set<Node*> visited; |
| 287 | std::deque<Node*> queue; |
| 288 | for (const Output& nout : inputs_) { |
| 289 | auto const& pair = visited.insert(nout.node()); |
| 290 | if (pair.second) { |
| 291 | queue.push_back(nout.node()); |
| 292 | } |
| 293 | } |
| 294 | |
| 295 | // Going forward to figure out which endpoints need backprop-ed. |
| 296 | // A node's endpoints need to be backprop-ed only if one of the |
| 297 | // arg node can reach the node via data edges. |
| 298 | while (!queue.empty()) { |
| 299 | Node* n = queue.front(); |
| 300 | queue.pop_front(); |
| 301 | for (int i = 0; i < n->num_outputs(); ++i) { |
| 302 | backprops_[{n, i}].clear(); |
| 303 | } |
| 304 | int num_expected_backprops = 0; |
| 305 | if (stop_backprop_nodes.find(n->id()) == stop_backprop_nodes.end()) { |
| 306 | // Internal node: continue BFS along connected outputs. |
nothing calls this directly
no test coverage detected