// Copyright (c) 2024 PaddlePaddle Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #pragma once #include #include "glog/logging.h" #include "paddle/ap/include/graph/adt.h" #include "paddle/ap/include/graph/graph_descriptor.h" #include "paddle/ap/include/graph/node.h" #include "paddle/ap/include/graph/node_arena.h" namespace ap::graph { template struct GraphHelper { explicit GraphHelper(const GraphDescriptor& graph_descriptor) : graph_descriptor_(graph_descriptor) {} GraphHelper(const GraphHelper&) = delete; GraphHelper(GraphHelper&&) = delete; adt::Result FindAnchor(const NodeT& start) { const auto& True = [](const auto&) -> adt::Result { return true; }; ADT_LET_CONST_REF(opt_anchor, FilterAnchor(start, True)); ADT_CHECK(opt_anchor.has_value()) << adt::errors::MismatchError{}; return opt_anchor.value(); } template adt::Result> FilterAnchor(const NodeT& start, const FilterT& Filter) { const auto topo_walker = GetTopoWalker(); const auto IsSource = [&](const NodeT& sg_node) -> adt::Result { bool has_source = false; auto SetHasSource = [&](const NodeT&) -> adt::Result { has_source = true; return adt::Ok{}; }; ADT_RETURN_IF_ERR( graph_descriptor_.VisitUpstreamNodes(sg_node, SetHasSource)); return !has_source; }; const auto IsSink = [&](const NodeT& sg_node) -> adt::Result { bool has_sink = false; auto SetHasSink = [&](const NodeT&) -> adt::Result { has_sink = true; return adt::Ok{}; }; ADT_RETURN_IF_ERR( graph_descriptor_.VisitDownstreamNodes(sg_node, SetHasSink)); return !has_sink; }; std::unordered_set source_or_sinks; auto CollectStarts = [&](const NodeT& sg_node) -> adt::Result { ADT_LET_CONST_REF(ignored, graph_descriptor_.IgnoredNode(sg_node)); if (ignored) { return adt::Ok{}; } ADT_LET_CONST_REF(is_source, IsSource(sg_node)); ADT_LET_CONST_REF(is_sink, IsSink(sg_node)); if (is_source || is_sink) { source_or_sinks.insert(sg_node); } return adt::Ok{}; }; const auto bfs_walker_without_ignore = GetBfsWalkerWithoutIgnore(); ADT_RETURN_IF_ERR(bfs_walker_without_ignore(start, CollectStarts)); ADT_CHECK(source_or_sinks.size() > 0); std::unordered_map node2depth; std::map> depth2nodes; const auto bfs_walker = GetBfsWalker(); auto UpdateNodeDepth = [&](const NodeT& sg_node) -> adt::Result { size_t max_depth = 0; ADT_RETURN_IF_ERR(bfs_walker.VisitNextNodes( sg_node, [&](const NodeT& prev) -> adt::Result { const auto& iter = node2depth.find(prev); if (iter != node2depth.end()) { max_depth = std::max(max_depth, iter->second); } return adt::Ok{}; })); node2depth[sg_node] = max_depth; depth2nodes[max_depth].push_back(sg_node); return adt::Ok{}; }; ADT_RETURN_IF_ERR(bfs_walker( source_or_sinks.begin(), source_or_sinks.end(), UpdateNodeDepth)); for (auto iter = depth2nodes.rbegin(); iter != depth2nodes.rend(); ++iter) { for (const auto& node : iter->second) { ADT_LET_CONST_REF(is_op_node, this->graph_descriptor_.IsOpNode(node)); if (is_op_node) { ADT_LET_CONST_REF(filter_success, Filter(node)); if (filter_success) { return node; } } } } return std::nullopt; } adt::BfsWalker GetBfsWalker() { auto graph = this->graph_descriptor_; const auto& ForEachNext = [graph](const NodeT& node, const auto& VisitNext) -> adt::Result { auto DoEach = [&](const NodeT& next) -> adt::Result { ADT_LET_CONST_REF(is_ignored, graph.IgnoredNode(next)); if (is_ignored) { return adt::Ok{}; } return VisitNext(next); }; ADT_RETURN_IF_ERR(graph.VisitDownstreamNodes(node, DoEach)); ADT_RETURN_IF_ERR(graph.VisitUpstreamNodes(node, DoEach)); return adt::Ok{}; }; return adt::BfsWalker(ForEachNext); } adt::BfsWalker GetBfsWalkerWithoutIgnore() { auto graph = this->graph_descriptor_; const auto& ForEachNext = [graph](const NodeT& node, const auto& VisitNext) -> adt::Result { ADT_RETURN_IF_ERR(graph.VisitDownstreamNodes(node, VisitNext)); ADT_RETURN_IF_ERR(graph.VisitUpstreamNodes(node, VisitNext)); return adt::Ok{}; }; return adt::BfsWalker(ForEachNext); } adt::TopoWalker GetTopoWalker() { auto graph = this->graph_descriptor_; const auto& ForEachPrev = [graph](const NodeT& node, const auto& VisitPrev) -> adt::Result { auto DoEach = [&](const NodeT& prev) -> adt::Result { ADT_LET_CONST_REF(is_ignored, graph.IgnoredNode(prev)); if (is_ignored) { return adt::Ok{}; } return VisitPrev(prev); }; return graph.VisitUpstreamNodes(node, DoEach); }; const auto& ForEachNext = [graph](const NodeT& node, const auto& VisitNext) -> adt::Result { auto DoEach = [&](const NodeT& next) -> adt::Result { ADT_LET_CONST_REF(is_ignored, graph.IgnoredNode(next)); if (is_ignored) { return adt::Ok{}; } return VisitNext(next); }; return graph.VisitDownstreamNodes(node, DoEach); }; return adt::TopoWalker(ForEachPrev, ForEachNext); } private: GraphDescriptor graph_descriptor_; }; } // namespace ap::graph