| 1489 | } |
| 1490 | |
| 1491 | Status MutableGraphView::CheckNodesCanBeDeleted( |
| 1492 | const absl::flat_hash_set<string>& nodes_to_delete) { |
| 1493 | std::vector<string> missing_nodes; |
| 1494 | std::vector<string> nodes_with_fanouts; |
| 1495 | for (const string& node_name_to_delete : nodes_to_delete) { |
| 1496 | NodeDef* node = GetNode(node_name_to_delete); |
| 1497 | if (node == nullptr) { |
| 1498 | // Can't delete missing node. |
| 1499 | missing_nodes.push_back(node_name_to_delete); |
| 1500 | continue; |
| 1501 | } |
| 1502 | const int max_port = gtl::FindWithDefault(max_regular_output_port(), node, |
| 1503 | Graph::kControlSlot); |
| 1504 | for (int i = Graph::kControlSlot; i <= max_port; ++i) { |
| 1505 | auto it = fanouts().find({node, i}); |
| 1506 | bool has_retained_fanout = false; |
| 1507 | if (it != fanouts().end()) { |
| 1508 | for (const auto& fanout : it->second) { |
| 1509 | // Check if fanouts are of nodes to be deleted, and if so, they can be |
| 1510 | // ignored, as they will be removed also. |
| 1511 | if (!nodes_to_delete.contains(fanout.node->name())) { |
| 1512 | // Removing node will leave graph in an invalid state. |
| 1513 | has_retained_fanout = true; |
| 1514 | break; |
| 1515 | } |
| 1516 | } |
| 1517 | } |
| 1518 | if (has_retained_fanout) { |
| 1519 | nodes_with_fanouts.push_back(node_name_to_delete); |
| 1520 | break; |
| 1521 | } |
| 1522 | } |
| 1523 | } |
| 1524 | |
| 1525 | // Error message can get quite long, so we only show the first 5 node names. |
| 1526 | auto sort_and_sample = [](std::vector<string>* s) { |
| 1527 | constexpr int kMaxNodeNames = 5; |
| 1528 | std::sort(s->begin(), s->end()); |
| 1529 | if (s->size() > kMaxNodeNames) { |
| 1530 | return absl::StrCat( |
| 1531 | absl::StrJoin(s->begin(), s->begin() + kMaxNodeNames, ", "), ", ..."); |
| 1532 | } |
| 1533 | return absl::StrJoin(*s, ", "); |
| 1534 | }; |
| 1535 | |
| 1536 | if (!missing_nodes.empty()) { |
| 1537 | VLOG(2) << absl::Substitute("Attempting to delete missing node(s) [$0].", |
| 1538 | sort_and_sample(&missing_nodes)); |
| 1539 | } |
| 1540 | if (!nodes_with_fanouts.empty()) { |
| 1541 | std::vector<string> input_node_names(nodes_to_delete.begin(), |
| 1542 | nodes_to_delete.end()); |
| 1543 | string params = absl::Substitute("nodes_to_delete={$0}", |
| 1544 | sort_and_sample(&input_node_names)); |
| 1545 | string error_msg = |
| 1546 | absl::Substitute("can't delete node(s) with retained fanouts(s) [$0]", |
| 1547 | sort_and_sample(&nodes_with_fanouts)); |
| 1548 | return MutationError("DeleteNodes", params, error_msg); |