Files
2026-07-13 12:40:42 +08:00

329 lines
10 KiB
C++

// Copyright (c) 2021 CINN 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
//! \file This file contains the utilities of graph.
#include <glog/logging.h>
#include <algorithm>
#include <functional>
#include <list>
#include <map>
#include <memory>
#include <set>
#include <string>
#include <tuple>
#include <vector>
#include "paddle/cinn/common/object.h"
#include "paddle/cinn/common/shared.h"
#include "paddle/cinn/common/type.h"
#include "paddle/common/enforce.h"
#include "paddle/utils/flat_hash_map.h"
namespace cinn {
namespace common {
#ifdef As
#undef As
#endif
class GraphNode;
/**
* Edge in the graph, which can hold some attributes.
*/
class GraphEdge : public Object {
public:
GraphEdge(GraphNode* source, GraphNode* sink, int index = -1)
: source_(source), sink_(sink), index_(index) {}
GraphNode* source() const { return source_; }
GraphNode* sink() const { return sink_; }
const char* type_info() const override { return __type_info__; }
int index() const { return index_; }
private:
//! the index in sink node's inlinks_ or source node's outlinks_
//! this is used to keep the input/output tensor's order of operator node
int index_{-1};
//! Source of this edge.
GraphNode* source_{};
//! End of this edge.
GraphNode* sink_{};
static constexpr char* __type_info__ = "graph_edge";
};
struct GraphEdgeCompare {
bool operator()(const cinn::common::Shared<GraphEdge>& a,
const cinn::common::Shared<GraphEdge>& b) const;
};
/**
* @brief The base class of all node of graph.
* This is used to normalize and share the graph operations.
*/
class GraphNode : public Object {
public:
//! The unique identifier of the node.
virtual std::string id() const = 0;
inline int get_index() { return index; }
inline void set_index(int index) { this->index = index; }
//! Links from this to other.
template <typename EdgeT = GraphEdge>
std::tuple<EdgeT*, EdgeT*> LinkTo(GraphNode* other) {
EdgeT *a, *b;
PADDLE_ENFORCE_NOT_NULL(
other, ::common::errors::InvalidArgument("The input node is null."));
PADDLE_ENFORCE_NE(
other,
this,
::common::errors::InvalidArgument("Cannot link to itself"));
auto outlink_edge = make_shared<GraphEdge>(this, other, index_outlinks);
auto inlink_edge =
make_shared<GraphEdge>(this, other, other->index_inlinks);
index_outlinks++;
other->index_inlinks++;
outlinks_.insert(outlink_edge);
other->inlinks_.insert(inlink_edge);
for (auto& item : outlinks_) {
if (item->index() == index_outlinks - 1) {
a = static_cast<EdgeT*>(item.get());
break;
}
}
for (auto& item : other->inlinks_) {
if (item->index() == other->index_inlinks - 1) {
b = static_cast<EdgeT*>(item.get());
break;
}
}
PADDLE_ENFORCE_NOT_NULL(
a, ::common::errors::InvalidArgument("Sorry,but outlinks is nullptr"));
PADDLE_ENFORCE_NOT_NULL(b,
::common::errors::InvalidArgument(
"Sorry, but other->inlinks_ is nullptr"));
return std::make_tuple(a, b);
}
void Controls(GraphNode* other) {
bool outlink_linked = false;
bool inlink_linked = false;
for (auto& item : outlinks_) {
if (item->sink()->id() == other->id()) {
outlink_linked = true;
break;
}
}
for (auto& item : other->inlinks_) {
if (item->source()->id() == this->id()) {
inlink_linked = true;
break;
}
}
PADDLE_ENFORCE_EQ(outlink_linked,
inlink_linked,
::common::errors::InvalidArgument(
"The outlink_linked should same as inlink_linked."));
if (outlink_linked)
return;
else
this->LinkTo(other);
}
void UnLinkAllTo(GraphNode* other) {
if (other == this) return;
// remove all this node's outlink
{
auto it = std::find_if(
outlinks_.begin(), outlinks_.end(), [&](const Shared<GraphEdge>& x) {
return x->source() == this && x->sink() == other;
});
while (it != outlinks_.end()) {
outlinks_.erase(it);
it = std::find_if(outlinks_.begin(),
outlinks_.end(),
[&](const Shared<GraphEdge>& x) {
return x->source() == this && x->sink() == other;
});
}
}
// remove all other node's inlink
{
auto it = std::find_if(other->inlinks_.begin(),
other->inlinks_.end(),
[&](const Shared<GraphEdge>& x) {
return x->source() == this && x->sink() == other;
});
while (it != other->inlinks_.end()) {
other->inlinks_.erase(it);
it = std::find_if(other->inlinks_.begin(),
other->inlinks_.end(),
[&](const Shared<GraphEdge>& x) {
return x->source() == this && x->sink() == other;
});
}
}
}
void UnLinkSingleTo(GraphNode* other) {
if (other == this) return;
// remove single outlink
{
auto it = std::find_if(
outlinks_.begin(), outlinks_.end(), [&](const Shared<GraphEdge>& x) {
return x->source() == this && x->sink() == other;
});
if (it != outlinks_.end()) outlinks_.erase(it);
}
// remove single inlink
{
auto it = std::find_if(other->inlinks_.begin(),
other->inlinks_.end(),
[&](const Shared<GraphEdge>& x) {
return x->source() == this && x->sink() == other;
});
if (it != other->inlinks_.end()) other->inlinks_.erase(it);
}
}
bool IsLinkedTo(GraphNode* other) const {
for (auto& e : outlinks_) {
if (e->sink()->id() == other->id()) return true;
}
return false;
}
//! Get the input links of the node.
virtual const std::set<Shared<GraphEdge>, GraphEdgeCompare>& inlinks() const {
return inlinks_;
}
//! Get the output links of the node.
virtual const std::set<Shared<GraphEdge>, GraphEdgeCompare>& outlinks()
const {
return outlinks_;
}
//! Reset graph traversal meta info.
void ResetVisitMeta() { visited_time_ = 0; }
void VisitOnce() const { visited_time_++; }
bool visited() const {
return inlinks_.empty() || visited_time_ == inlinks_.size();
}
const char* type_info() const override { return __type_info__; }
GraphNode() = default;
static const char* __type_info__;
protected:
//! The input links of the node.
//! \note We record the raw pointer rather than the shared pointer to avoid
//! cycle reference.
std::set<cinn::common::Shared<GraphEdge>, GraphEdgeCompare> inlinks_;
//! The output links of the node.
//! \note We record the raw pointer rather than the shared pointer to avoid
//! cycle reference.
std::set<cinn::common::Shared<GraphEdge>, GraphEdgeCompare> outlinks_;
mutable int visited_time_{};
//! used to mark the index of node's input/output tensors
int index_inlinks{0};
int index_outlinks{0};
int index{0};
};
/**
* @brief The base class of all the graph.
*/
class Graph {
public:
using node_order_t = std::vector<GraphNode*>;
using edge_order_t = std::vector<GraphEdge*>;
//! Add a node to the graph.
//! @{
GraphNode* RegisterNode(size_t key, GraphNode* node);
GraphNode* RegisterNode(const std::string& key, GraphNode* node);
//! @}
//! Retrieve a node.
//! @{
GraphNode* RetrieveNode(size_t key) const;
GraphNode* RetrieveNode(const std::string& key) const;
//! @}
//! Get the start point of the graph (the nodes those has no inlinks).
std::vector<const GraphNode*> start_points() const;
std::vector<GraphNode*> start_points();
//! Return the graph's nodes and edges(visited) in topological order.
std::tuple<std::vector<GraphNode*>, std::vector<GraphEdge*>>
topological_order() const;
//! Return the graph's DFS order.
std::vector<GraphNode*> dfs_order();
//! Return the dependency nodes of a set of nodes.
std::set<GraphNode*> dependencies(const std::vector<GraphNode*>& nodes);
std::vector<const GraphNode*> nodes() const;
std::vector<GraphNode*> nodes();
//! Collect the nodes match the condition defined by \p teller in the graph.
std::set<GraphNode*> CollectNodes(
std::function<bool(const cinn::common::GraphNode*)>&& teller);
void DropNode(GraphNode* n) {
auto it = std::find_if(
nodes_.begin(), nodes_.end(), [&](auto& x) { return x.get() == n; });
if (it != nodes_.end()) {
nodes_.erase(it);
}
}
//! Get a string representation to visualize a graph.
std::string Visualize() const;
void ClearUnlinkedNodes(
paddle::flat_hash_map<std::string, std::vector<int>>* shape_dict,
paddle::flat_hash_map<std::string, cinn::common::Type>* type_dict,
paddle::flat_hash_map<std::string, std::string>* layout_dict);
size_t num_nodes() const { return nodes_.size(); }
protected:
//! A lookup table that map from hash key to graph node, note that it doesn't
//! own the graph node.
std::map<size_t, GraphNode*> registry_;
//! A list owns the graph nodes.
std::vector<Shared<GraphNode>> nodes_;
};
} // namespace common
} // namespace cinn
namespace std {
template <>
struct hash<cinn::common::GraphNode> {
size_t operator()(const cinn::common::GraphNode& x) {
return reinterpret_cast<size_t>(hash<std::string>()(x.id()));
}
};
} // namespace std