souffle  2.0.2-371-g6315b36
TopologicallySortedSCCGraph.cpp
Go to the documentation of this file.
1 /*
2  * Souffle - A Datalog Compiler
3  * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved
4  * Licensed under the Universal Permissive License v 1.0 as shown at:
5  * - https://opensource.org/licenses/UPL
6  * - <souffle root>/licenses/SOUFFLE-UPL.txt
7  */
8 
9 /************************************************************************
10  *
11  * @file TopologicallySortedSCCGraph.cpp
12  *
13  * Implements method of precedence graph to build the precedence graph,
14  * compute strongly connected components of the precedence graph, and
15  * build the strongly connected component graph.
16  *
17  ***********************************************************************/
18 
20 #include "ast/QualifiedName.h"
21 #include "ast/Relation.h"
22 #include "ast/TranslationUnit.h"
23 #include "ast/analysis/SCCGraph.h"
25 #include <algorithm>
26 #include <set>
27 #include <string>
28 
29 namespace souffle::ast::analysis {
30 
32  const std::vector<size_t>& permutationOfSCCs) const {
33  // create variables to hold the cost of the current SCC and the permutation as a whole
34  int costOfSCC = 0;
35  int costOfPermutation = -1;
36  // obtain an iterator to the end of the already ordered partition of sccs
37  auto it_k = permutationOfSCCs.begin() + sccOrder.size();
38  // for each of the scc's in the ordering, resetting the cost of the scc to zero on each loop
39  for (auto it_i = permutationOfSCCs.begin(); it_i != permutationOfSCCs.end(); ++it_i, costOfSCC = 0) {
40  // if the index of the current scc is after the end of the ordered partition
41  if (it_i >= it_k) {
42  // check that the index of all predecessor sccs of are before the index of the current scc
43  for (auto scc : sccGraph->getPredecessorSCCs(*it_i)) {
44  if (std::find(permutationOfSCCs.begin(), it_i, scc) == it_i) {
45  // if not, the sort is not a valid topological sort
46  return -1;
47  }
48  }
49  }
50  // otherwise, calculate the cost of the current scc
51  // as the number of sccs with an index before the current scc
52  for (auto it_j = permutationOfSCCs.begin(); it_j != it_i; ++it_j) {
53  // having some successor scc with an index after the current scc
54  for (auto scc : sccGraph->getSuccessorSCCs(*it_j)) {
55  if (std::find(permutationOfSCCs.begin(), it_i, scc) == it_i) {
56  costOfSCC++;
57  }
58  }
59  }
60  // and if this cost is greater than the maximum recorded cost for the whole permutation so far,
61  // set the cost of the permutation to it
62  if (costOfSCC > costOfPermutation) {
63  costOfPermutation = costOfSCC;
64  }
65  }
66  return costOfPermutation;
67 }
68 
69 void TopologicallySortedSCCGraphAnalysis::computeTopologicalOrdering(size_t scc, std::vector<bool>& visited) {
70  // create a flag to indicate that a successor was visited (by default it hasn't been)
71  bool found = false;
72  bool hasUnvisitedSuccessor = false;
73  bool hasUnvisitedPredecessor = false;
74  // for each successor of the input scc
75  const auto& successorsToVisit = sccGraph->getSuccessorSCCs(scc);
76  for (const auto scc_i : successorsToVisit) {
77  if (visited[scc_i]) {
78  continue;
79  }
80  hasUnvisitedPredecessor = false;
81  const auto& successorsPredecessors = sccGraph->getPredecessorSCCs(scc_i);
82  for (const auto scc_j : successorsPredecessors) {
83  if (!visited[scc_j]) {
84  hasUnvisitedPredecessor = true;
85  break;
86  }
87  }
88  if (!hasUnvisitedPredecessor) {
89  // give it a temporary marking
90  visited[scc_i] = true;
91  // add it to the permanent ordering
92  sccOrder.push_back(scc_i);
93  // and use it as a root node in a recursive call to this function
94  computeTopologicalOrdering(scc_i, visited);
95  // finally, indicate that a successor has been found for this node
96  found = true;
97  }
98  }
99  // return at once if no valid successors have been found; as either it has none or they all have a
100  // better predecessor
101  if (!found) {
102  return;
103  }
104  hasUnvisitedPredecessor = false;
105  const auto& predecessors = sccGraph->getPredecessorSCCs(scc);
106  for (const auto scc_j : predecessors) {
107  if (!visited[scc_j]) {
108  hasUnvisitedPredecessor = true;
109  break;
110  }
111  }
112  hasUnvisitedSuccessor = false;
113  const auto& successors = sccGraph->getSuccessorSCCs(scc);
114  for (const auto scc_j : successors) {
115  if (!visited[scc_j]) {
116  hasUnvisitedSuccessor = true;
117  break;
118  }
119  }
120  // otherwise, if more white successors remain for the current scc, use it again as the root node in a
121  // recursive call to this function
122  if (hasUnvisitedSuccessor && !hasUnvisitedPredecessor) {
123  computeTopologicalOrdering(scc, visited);
124  }
125 }
126 
127 void TopologicallySortedSCCGraphAnalysis::run(const TranslationUnit& translationUnit) {
128  // obtain the scc graph
129  sccGraph = translationUnit.getAnalysis<SCCGraphAnalysis>();
130  // clear the list of ordered sccs
131  sccOrder.clear();
132  std::vector<bool> visited;
133  visited.resize(sccGraph->getNumberOfSCCs());
134  std::fill(visited.begin(), visited.end(), false);
135  // generate topological ordering using forwards algorithm (like Khan's algorithm)
136  // for each of the sccs in the graph
137  for (size_t scc = 0; scc < sccGraph->getNumberOfSCCs(); ++scc) {
138  // if that scc has no predecessors
139  if (sccGraph->getPredecessorSCCs(scc).empty()) {
140  // put it in the ordering
141  sccOrder.push_back(scc);
142  visited[scc] = true;
143  // if the scc has successors
144  if (!sccGraph->getSuccessorSCCs(scc).empty()) {
145  computeTopologicalOrdering(scc, visited);
146  }
147  }
148  }
149 }
150 
151 void TopologicallySortedSCCGraphAnalysis::print(std::ostream& os) const {
152  os << "--- partial order of strata as list of pairs ---" << std::endl;
153  for (size_t sccIndex = 0; sccIndex < sccOrder.size(); sccIndex++) {
154  const auto& successorSccs = sccGraph->getSuccessorSCCs(sccOrder.at(sccIndex));
155  // use a self-loop to indicate that an SCC has no successors or predecessors
156  if (successorSccs.empty() && sccGraph->getPredecessorSCCs(sccOrder.at(sccIndex)).empty()) {
157  os << sccIndex << " " << sccIndex << std::endl;
158  continue;
159  }
160  for (const auto successorScc : successorSccs) {
161  const auto successorSccIndex = *std::find(sccOrder.begin(), sccOrder.end(), successorScc);
162  os << sccIndex << " " << successorSccIndex << std::endl;
163  }
164  }
165  os << "--- total order with relations of each strata ---" << std::endl;
166  for (size_t i = 0; i < sccOrder.size(); i++) {
167  os << i << ": [";
169  [](std::ostream& out, const Relation* rel) { out << rel->getQualifiedName(); });
170  os << "]" << std::endl;
171  }
172  os << std::endl;
173  os << "--- statistics of topological order ---" << std::endl;
174  os << "cost: " << topologicalOrderingCost(sccOrder) << std::endl;
175 }
176 
177 } // namespace souffle::ast::analysis
TranslationUnit.h
souffle::ast::analysis::TopologicallySortedSCCGraphAnalysis::sccGraph
SCCGraphAnalysis * sccGraph
The strongly connected component (SCC) graph.
Definition: TopologicallySortedSCCGraph.h:84
souffle::ast::analysis::TopologicallySortedSCCGraphAnalysis::print
void print(std::ostream &os) const override
Output topologically sorted strongly connected component graph in text format.
Definition: TopologicallySortedSCCGraph.cpp:159
Relation.h
SCCGraph.h
souffle::Relation
Object-oriented wrapper class for Souffle's templatized relations.
Definition: SouffleInterface.h:49
i
size_t i
Definition: json11.h:663
TopologicallySortedSCCGraph.h
souffle::join
detail::joined_sequence< Iter, Printer > join(const Iter &a, const Iter &b, const std::string &sep, const Printer &p)
Creates an object to be forwarded to some output stream for printing sequences of elements interspers...
Definition: StreamUtil.h:175
souffle::ast::analysis::SCCGraphAnalysis::getNumberOfSCCs
size_t getNumberOfSCCs() const
Get the number of SCCs in the graph.
Definition: SCCGraph.h:59
souffle::ast::analysis::TopologicallySortedSCCGraphAnalysis::sccOrder
std::vector< size_t > sccOrder
The final topological ordering of the SCCs.
Definition: TopologicallySortedSCCGraph.h:87
souffle::ast::analysis::TopologicallySortedSCCGraphAnalysis::topologicalOrderingCost
int topologicalOrderingCost(const std::vector< size_t > &permutationOfSCCs) const
Calculate the topological ordering cost of a permutation of as of yet unordered SCCs using the ordere...
Definition: TopologicallySortedSCCGraph.cpp:39
QualifiedName.h
souffle::ast::analysis::TopologicallySortedSCCGraphAnalysis::computeTopologicalOrdering
void computeTopologicalOrdering(size_t scc, std::vector< bool > &visited)
Recursive component for the forwards algorithm computing the topological ordering of the SCCs.
Definition: TopologicallySortedSCCGraph.cpp:77
StreamUtil.h
souffle::ast::analysis
Definition: Aggregate.cpp:39
souffle::ast::analysis::SCCGraphAnalysis::getSuccessorSCCs
const std::set< size_t > & getSuccessorSCCs(const size_t scc) const
Get all successor SCCs of a given SCC.
Definition: SCCGraph.h:69
souffle::ast::analysis::SCCGraphAnalysis::getPredecessorSCCs
const std::set< size_t > & getPredecessorSCCs(const size_t scc) const
Get all predecessor SCCs of a given SCC.
Definition: SCCGraph.h:74
rel
void rel(size_t limit, bool showLimit=true)
Definition: Tui.h:1086
souffle::ast::analysis::SCCGraphAnalysis::getInternalRelations
const std::set< const Relation * > & getInternalRelations(const size_t scc) const
Get all internal relations of a given SCC.
Definition: SCCGraph.h:105
souffle::ast::analysis::TopologicallySortedSCCGraphAnalysis::run
void run(const TranslationUnit &translationUnit) override
run analysis for a Ast translation unit
Definition: TopologicallySortedSCCGraph.cpp:135