• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
2 
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6 
7     http://www.apache.org/licenses/LICENSE-2.0
8 
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15 
16 // GraphCycles provides incremental cycle detection on a dynamic
17 // graph using the following algorithm:
18 //
19 // A dynamic topological sort algorithm for directed acyclic graphs
20 // David J. Pearce, Paul H. J. Kelly
21 // Journal of Experimental Algorithmics (JEA) JEA Homepage archive
22 // Volume 11, 2006, Article No. 1.7
23 //
24 // Brief summary of the algorithm:
25 //
26 // (1) Maintain a rank for each node that is consistent
27 //     with the topological sort of the graph. I.e., path from x to y
28 //     implies rank[x] < rank[y].
29 // (2) When a new edge (x->y) is inserted, do nothing if rank[x] < rank[y].
30 // (3) Otherwise: adjust ranks in the neighborhood of x and y.
31 
32 #include "tensorflow/compiler/jit/graphcycles/graphcycles.h"
33 
34 #include <algorithm>
35 #include <unordered_set>
36 
37 #include "absl/container/inlined_vector.h"
38 #include "tensorflow/core/platform/logging.h"
39 
40 namespace tensorflow {
41 
42 namespace {
43 
44 typedef std::unordered_set<int32> NodeSet;
45 template <typename T>
46 struct VecStruct {
47   typedef absl::InlinedVector<T, 4> type;
48 };
49 template <typename T>
50 using Vec = typename VecStruct<T>::type;
51 
52 struct Node {
Nodetensorflow::__anon0349984b0111::Node53   Node() : in(4), out(4) {}  // Small hashtables for in/out edges
54 
55   int32 rank;    // rank number assigned by Pearce-Kelly algorithm
56   bool visited;  // Temporary marker used by depth-first-search
57   void* data;    // User-supplied data
58   NodeSet in;    // List of immediate predecessor nodes in graph
59   NodeSet out;   // List of immediate successor nodes in graph
60 };
61 
62 }  // namespace
63 
64 struct GraphCycles::Rep {
65   Vec<Node*> nodes_;
66   Vec<int32> free_nodes_;  // Indices for unused entries in nodes_
67 
68   // Temporary state.
69   Vec<int32> deltaf_;  // Results of forward DFS
70   Vec<int32> deltab_;  // Results of backward DFS
71   Vec<int32> list_;    // All nodes to reprocess
72   Vec<int32> merged_;  // Rank values to assign to list_ entries
73   Vec<int32> stack_;   // Emulates recursion stack when doing depth first search
74 };
75 
GraphCycles()76 GraphCycles::GraphCycles() : rep_(new Rep) {}
77 
~GraphCycles()78 GraphCycles::~GraphCycles() {
79   for (Vec<Node*>::size_type i = 0; i < rep_->nodes_.size(); i++) {
80     delete rep_->nodes_[i];
81   }
82   delete rep_;
83 }
84 
CheckInvariants() const85 bool GraphCycles::CheckInvariants() const {
86   Rep* r = rep_;
87   NodeSet ranks;  // Set of ranks seen so far.
88   for (Vec<Node*>::size_type x = 0; x < r->nodes_.size(); x++) {
89     Node* nx = r->nodes_[x];
90     if (nx->visited) {
91       LOG(FATAL) << "Did not clear visited marker on node " << x;
92     }
93     if (!ranks.insert(nx->rank).second) {
94       LOG(FATAL) << "Duplicate occurrence of rank " << nx->rank;
95     }
96     for (auto y : nx->out) {
97       Node* ny = r->nodes_[y];
98       if (nx->rank >= ny->rank) {
99         LOG(FATAL) << "Edge " << x << "->" << y << " has bad rank assignment "
100                    << nx->rank << "->" << ny->rank;
101       }
102     }
103   }
104   return true;
105 }
106 
NewNode()107 int32 GraphCycles::NewNode() {
108   if (rep_->free_nodes_.empty()) {
109     Node* n = new Node;
110     n->visited = false;
111     n->data = nullptr;
112     n->rank = rep_->nodes_.size();
113     rep_->nodes_.push_back(n);
114     return n->rank;
115   } else {
116     // Preserve preceding rank since the set of ranks in use must be
117     // a permutation of [0,rep_->nodes_.size()-1].
118     int32 r = rep_->free_nodes_.back();
119     rep_->nodes_[r]->data = nullptr;
120     rep_->free_nodes_.pop_back();
121     return r;
122   }
123 }
124 
RemoveNode(int32 node)125 void GraphCycles::RemoveNode(int32 node) {
126   Node* x = rep_->nodes_[node];
127   for (auto y : x->out) {
128     rep_->nodes_[y]->in.erase(node);
129   }
130   for (auto y : x->in) {
131     rep_->nodes_[y]->out.erase(node);
132   }
133   x->in.clear();
134   x->out.clear();
135   rep_->free_nodes_.push_back(node);
136 }
137 
GetNodeData(int32 node) const138 void* GraphCycles::GetNodeData(int32 node) const {
139   return rep_->nodes_[node]->data;
140 }
141 
SetNodeData(int32 node,void * data)142 void GraphCycles::SetNodeData(int32 node, void* data) {
143   rep_->nodes_[node]->data = data;
144 }
145 
HasEdge(int32 x,int32 y) const146 bool GraphCycles::HasEdge(int32 x, int32 y) const {
147   return rep_->nodes_[x]->out.find(y) != rep_->nodes_[x]->out.end();
148 }
149 
RemoveEdge(int32 x,int32 y)150 void GraphCycles::RemoveEdge(int32 x, int32 y) {
151   rep_->nodes_[x]->out.erase(y);
152   rep_->nodes_[y]->in.erase(x);
153   // No need to update the rank assignment since a previous valid
154   // rank assignment remains valid after an edge deletion.
155 }
156 
157 static bool ForwardDFS(GraphCycles::Rep* r, int32 n, int32 upper_bound);
158 static void BackwardDFS(GraphCycles::Rep* r, int32 n, int32 lower_bound);
159 static void Reorder(GraphCycles::Rep* r);
160 static void Sort(const Vec<Node*>&, Vec<int32>* delta);
161 static void MoveToList(GraphCycles::Rep* r, Vec<int32>* src, Vec<int32>* dst);
162 static void ClearVisitedBits(GraphCycles::Rep* r, const Vec<int32>& nodes);
163 
InsertEdge(int32 x,int32 y)164 bool GraphCycles::InsertEdge(int32 x, int32 y) {
165   if (x == y) return false;
166   Rep* r = rep_;
167   Node* nx = r->nodes_[x];
168   if (!nx->out.insert(y).second) {
169     // Edge already exists.
170     return true;
171   }
172 
173   Node* ny = r->nodes_[y];
174   ny->in.insert(x);
175 
176   if (nx->rank <= ny->rank) {
177     // New edge is consistent with existing rank assignment.
178     return true;
179   }
180 
181   // Current rank assignments are incompatible with the new edge.  Recompute.
182   // We only need to consider nodes that fall in the range [ny->rank,nx->rank].
183   if (!ForwardDFS(r, y, nx->rank)) {
184     // Found a cycle.  Undo the insertion and tell caller.
185     nx->out.erase(y);
186     ny->in.erase(x);
187     // Since we do not call Reorder() on this path, clear any visited
188     // markers left by ForwardDFS.
189     ClearVisitedBits(r, r->deltaf_);
190     return false;
191   }
192   BackwardDFS(r, x, ny->rank);
193   Reorder(r);
194   return true;
195 }
196 
ForwardDFS(GraphCycles::Rep * r,int32 n,int32 upper_bound)197 static bool ForwardDFS(GraphCycles::Rep* r, int32 n, int32 upper_bound) {
198   // Avoid recursion since stack space might be limited.
199   // We instead keep a stack of nodes to visit.
200   r->deltaf_.clear();
201   r->stack_.clear();
202   r->stack_.push_back(n);
203   while (!r->stack_.empty()) {
204     n = r->stack_.back();
205     r->stack_.pop_back();
206     Node* nn = r->nodes_[n];
207     if (nn->visited) continue;
208 
209     nn->visited = true;
210     r->deltaf_.push_back(n);
211 
212     for (auto w : nn->out) {
213       Node* nw = r->nodes_[w];
214       if (nw->rank == upper_bound) {
215         return false;  // Cycle
216       }
217       if (!nw->visited && nw->rank < upper_bound) {
218         r->stack_.push_back(w);
219       }
220     }
221   }
222   return true;
223 }
224 
BackwardDFS(GraphCycles::Rep * r,int32 n,int32 lower_bound)225 static void BackwardDFS(GraphCycles::Rep* r, int32 n, int32 lower_bound) {
226   r->deltab_.clear();
227   r->stack_.clear();
228   r->stack_.push_back(n);
229   while (!r->stack_.empty()) {
230     n = r->stack_.back();
231     r->stack_.pop_back();
232     Node* nn = r->nodes_[n];
233     if (nn->visited) continue;
234 
235     nn->visited = true;
236     r->deltab_.push_back(n);
237 
238     for (auto w : nn->in) {
239       Node* nw = r->nodes_[w];
240       if (!nw->visited && lower_bound < nw->rank) {
241         r->stack_.push_back(w);
242       }
243     }
244   }
245 }
246 
Reorder(GraphCycles::Rep * r)247 static void Reorder(GraphCycles::Rep* r) {
248   Sort(r->nodes_, &r->deltab_);
249   Sort(r->nodes_, &r->deltaf_);
250 
251   // Adds contents of delta lists to list_ (backwards deltas first).
252   r->list_.clear();
253   MoveToList(r, &r->deltab_, &r->list_);
254   MoveToList(r, &r->deltaf_, &r->list_);
255 
256   // Produce sorted list of all ranks that will be reassigned.
257   r->merged_.resize(r->deltab_.size() + r->deltaf_.size());
258   std::merge(r->deltab_.begin(), r->deltab_.end(), r->deltaf_.begin(),
259              r->deltaf_.end(), r->merged_.begin());
260 
261   // Assign the ranks in order to the collected list.
262   for (Vec<int32>::size_type i = 0; i < r->list_.size(); i++) {
263     r->nodes_[r->list_[i]]->rank = r->merged_[i];
264   }
265 }
266 
Sort(const Vec<Node * > & nodes,Vec<int32> * delta)267 static void Sort(const Vec<Node*>& nodes, Vec<int32>* delta) {
268   struct ByRank {
269     const Vec<Node*>* nodes;
270     bool operator()(int32 a, int32 b) const {
271       return (*nodes)[a]->rank < (*nodes)[b]->rank;
272     }
273   };
274   ByRank cmp;
275   cmp.nodes = &nodes;
276   std::sort(delta->begin(), delta->end(), cmp);
277 }
278 
MoveToList(GraphCycles::Rep * r,Vec<int32> * src,Vec<int32> * dst)279 static void MoveToList(GraphCycles::Rep* r, Vec<int32>* src, Vec<int32>* dst) {
280   for (Vec<int32>::size_type i = 0; i < src->size(); i++) {
281     int32 w = (*src)[i];
282     (*src)[i] = r->nodes_[w]->rank;  // Replace src entry with its rank
283     r->nodes_[w]->visited = false;   // Prepare for future DFS calls
284     dst->push_back(w);
285   }
286 }
287 
ClearVisitedBits(GraphCycles::Rep * r,const Vec<int32> & nodes)288 static void ClearVisitedBits(GraphCycles::Rep* r, const Vec<int32>& nodes) {
289   for (Vec<int32>::size_type i = 0; i < nodes.size(); i++) {
290     r->nodes_[nodes[i]]->visited = false;
291   }
292 }
293 
FindPath(int32 x,int32 y,int max_path_len,int32 path[]) const294 int GraphCycles::FindPath(int32 x, int32 y, int max_path_len,
295                           int32 path[]) const {
296   // Forward depth first search starting at x until we hit y.
297   // As we descend into a node, we push it onto the path.
298   // As we leave a node, we remove it from the path.
299   int path_len = 0;
300 
301   Rep* r = rep_;
302   NodeSet seen;
303   r->stack_.clear();
304   r->stack_.push_back(x);
305   while (!r->stack_.empty()) {
306     int32 n = r->stack_.back();
307     r->stack_.pop_back();
308     if (n < 0) {
309       // Marker to indicate that we are leaving a node
310       path_len--;
311       continue;
312     }
313 
314     if (path_len < max_path_len) {
315       path[path_len] = n;
316     }
317     path_len++;
318     r->stack_.push_back(-1);  // Will remove tentative path entry
319 
320     if (n == y) {
321       return path_len;
322     }
323 
324     for (auto w : r->nodes_[n]->out) {
325       if (seen.insert(w).second) {
326         r->stack_.push_back(w);
327       }
328     }
329   }
330 
331   return 0;
332 }
333 
IsReachable(int32 x,int32 y) const334 bool GraphCycles::IsReachable(int32 x, int32 y) const {
335   return FindPath(x, y, 0, nullptr) > 0;
336 }
337 
IsReachableNonConst(int32 x,int32 y)338 bool GraphCycles::IsReachableNonConst(int32 x, int32 y) {
339   if (x == y) return true;
340   Rep* r = rep_;
341   Node* nx = r->nodes_[x];
342   Node* ny = r->nodes_[y];
343 
344   if (nx->rank >= ny->rank) {
345     // x cannot reach y since it is after it in the topological ordering
346     return false;
347   }
348 
349   // See if x can reach y using a DFS search that is limited to y's rank
350   bool reachable = !ForwardDFS(r, x, ny->rank);
351 
352   // Clear any visited markers left by ForwardDFS.
353   ClearVisitedBits(r, r->deltaf_);
354   return reachable;
355 }
356 
CanContractEdge(int32 a,int32 b)357 bool GraphCycles::CanContractEdge(int32 a, int32 b) {
358   CHECK(HasEdge(a, b)) << "No edge exists from " << a << " to " << b;
359   RemoveEdge(a, b);
360   bool reachable = IsReachableNonConst(a, b);
361   // Restore the graph to its original state.
362   InsertEdge(a, b);
363   // If reachable, then contracting edge will cause cycle.
364   return !reachable;
365 }
366 
ContractEdge(int32 a,int32 b)367 bool GraphCycles::ContractEdge(int32 a, int32 b) {
368   CHECK(HasEdge(a, b));
369   RemoveEdge(a, b);
370 
371   if (IsReachableNonConst(a, b)) {
372     // Restore the graph to its original state.
373     InsertEdge(a, b);
374     return false;
375   }
376 
377   Node* nb = rep_->nodes_[b];
378   std::unordered_set<int32> out = std::move(nb->out);
379   std::unordered_set<int32> in = std::move(nb->in);
380   for (auto y : out) {
381     rep_->nodes_[y]->in.erase(b);
382   }
383   for (auto y : in) {
384     rep_->nodes_[y]->out.erase(b);
385   }
386   rep_->free_nodes_.push_back(b);
387 
388   for (auto y : out) {
389     InsertEdge(a, y);
390   }
391   for (auto y : in) {
392     InsertEdge(y, a);
393   }
394   return true;
395 }
396 
Successors(int32 node)397 std::unordered_set<int32> GraphCycles::Successors(int32 node) {
398   return rep_->nodes_[node]->out;
399 }
400 
Predecessors(int32 node)401 std::unordered_set<int32> GraphCycles::Predecessors(int32 node) {
402   return rep_->nodes_[node]->in;
403 }
404 
405 }  // namespace tensorflow
406