Skip to content

Commit

Permalink
Implement kahn's algorithm for topological sorting (#259)
Browse files Browse the repository at this point in the history
  • Loading branch information
pradkrish authored Jan 10, 2023
1 parent bcce7bf commit 9c05b35
Show file tree
Hide file tree
Showing 4 changed files with 273 additions and 17 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ add_test(test_rw_output test_exe --gtest_filter=RWOutputTest*)
add_test(test_partition test_exe --gtest_filter=PartitionTest*)
add_test(test_dial test_exe --gtest_filter=DialTest*)
add_test(test_bestfirstsearch test_exe --gtest_filter=BestFirstSearch*)
add_test(test_kahn test_exe --gtest_filter=Kahn*)

option(BENCHMARK "Enable Benchmark" OFF)
if(BENCHMARK)
Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ If you are interested, please contact us at [email protected] or contribute to
- [Graph Slicing based on connectivity](#graph-slicing-based-on-connectivity)
- [Ford-Fulkerson Algorithm](#ford-fulkerson-algorithm)
- [Kosaraju's Algorithm](#kosarajus-algorithm)
- [Kahn's Algorithm](#kahn-algorithm)
- [Partition Algorithm Explanation](#partition-algorithm-explanation)
- [Vertex-Cut](#vertex-cut)
- [Edge Balanced Vertex-Cut](#edge-balanced-vertex-cut)
Expand Down Expand Up @@ -506,6 +507,10 @@ The idea behind the algorithm is as follows: as long as there is a path from the
2). Reverse directions of all arcs to obtain the transpose graph.
3). One by one pop a vertex from S while S is not empty. Let the popped vertex be ‘v’. Take v as source and do DFS (call DFSUtil(v)). The DFS starting from v prints strongly connected component of v.

### Kahn's Algorithm
[Kahn's Algorithm](https://en.wikipedia.org/wiki/Topological_sorting#Kahn's_algorithm) finds topological
ordering by iteratively removing nodes in the graph which have no incoming edges. When a node is removed from the graph, it is added to the topological ordering and all its edges are removed allowing for the next set of nodes with no incoming edges to be selected.

## Partition Algorithm Explanation

### Vertex-Cut
Expand Down
117 changes: 100 additions & 17 deletions include/Graph/Graph.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -279,10 +279,10 @@ namespace CXXGRAPH
* search.
*
*/
virtual const std::vector<Node<T>> breadth_first_search(const Node<T> &start) const;
virtual const std::vector<Node<T>> breadth_first_search(const Node<T> &start) const;
/**
* \brief
* The multithreaded version of breadth_first_search
* The multithreaded version of breadth_first_search
* It turns out to be two indepentent functions because of implemntation differences
*
* @param start Node from where traversing starts
Expand Down Expand Up @@ -381,10 +381,20 @@ namespace CXXGRAPH
* @brief This function sort nodes in topological order.
* Applicable for Directed Acyclic Graph
*
* @return a vector containing nodes in topological order
* @return a struct with a vector of Nodes ordered topologically else ERROR in case
* of undirected or cyclic graph
*/
virtual TopoSortResult<T> topologicalSort() const;

/**
* @brief This function sort nodes in topological order using kahn's algorithm
* Applicable for Directed Acyclic Graph
*
* @return a struct with a vector of Nodes ordered topologically else ERROR in case
* of undirected or cyclic graph
*/
virtual TopoSortResult<T> kahn() const;

/**
* \brief
* This function performs performs the kosaraju algorthm on the graph to find the strongly connected components.
Expand Down Expand Up @@ -1010,7 +1020,7 @@ namespace CXXGRAPH
template <typename T>
const AdjacencyMatrix<T> Graph<T>::getAdjMatrix() const
{

AdjacencyMatrix<T> adj;
auto addElementToAdjMatrix = [&adj](const Node<T> *nodeFrom, const Node<T> *nodeTo, const Edge<T> *edge){
std::pair<const Node<T> *, const Edge<T> *> elem = {nodeTo, edge};
Expand Down Expand Up @@ -1726,7 +1736,7 @@ namespace CXXGRAPH
return bfs_result;
}

std::unordered_map<const Node<T> *, int> node_to_index;
std::unordered_map<const Node<T> *, int> node_to_index;
for (const auto &node : nodeSet)
{
node_to_index[node] = node_to_index.size();
Expand All @@ -1751,14 +1761,14 @@ namespace CXXGRAPH
level_tracker.push_back(&start);

// a worker is assigned a small part of tasks for each time
// assignments of tasks in current level and updates of tasks in next level are inclusive
// assignments of tasks in current level and updates of tasks in next level are inclusive
std::mutex tracker_mutex;
std::mutex next_tracker_mutex;
std::mutex next_tracker_mutex;
std::atomic<int> assigned_tasks = 0;
int num_tasks = 1;
// unit of task assignment, which mean assign block_size tasks to a worker each time
int block_size = 1;
int level = 1;
int level = 1;

auto extract_tasks = [&level_tracker, &tracker_mutex, &assigned_tasks, &num_tasks, &block_size] () -> std::pair<int,int>
{
Expand Down Expand Up @@ -1802,7 +1812,7 @@ namespace CXXGRAPH

for (int i = start_index; i < end_index; ++i)
{
if (adj.count(level_tracker[i]))
if (adj.count(level_tracker[i]))
{
for (const auto &elem : adj.at(level_tracker[i]))
{
Expand All @@ -1815,15 +1825,15 @@ namespace CXXGRAPH
}
}
}
}
}

// submit local result to global result
if (!local_tracker.empty())
{
submit_result(local_tracker);
}

// last worker need to do preparation for the next iteration
// last worker need to do preparation for the next iteration
int cur_level = level;
if (num_threads == 1 + waiting_workers.fetch_add(1))
{
Expand Down Expand Up @@ -1851,9 +1861,9 @@ namespace CXXGRAPH
{
// not to wait if last worker reachs last statement before notify all or even further
std::unique_lock<std::mutex> next_level_lock(next_level_mutex);
next_level_cond.wait(next_level_lock,
next_level_cond.wait(next_level_lock,
[&level, cur_level] () { return level != cur_level;});
}
}
}
};

Expand Down Expand Up @@ -2252,7 +2262,7 @@ namespace CXXGRAPH
{
TopoSortResult<T> result;
result.success = false;

if (!isDirectedGraph())
{
result.errorMessage = ERR_UNDIR_GRAPH;
Expand All @@ -2263,7 +2273,7 @@ namespace CXXGRAPH
result.errorMessage = ERR_CYCLIC_GRAPH;
return result;
}
else
else
{
const auto &adjMatrix = getAdjMatrix();
const auto &nodeSet = getNodeSet();
Expand All @@ -2278,15 +2288,15 @@ namespace CXXGRAPH
for (const auto &edge : adjMatrix.at(curNode))
{
const auto &nextNode = edge.first;
if (false == visited[nextNode])
if (false == visited[nextNode])
{
postorder_helper(nextNode);
}
}
}

result.nodesInTopoOrder.push_back(*curNode);
};
};

int numNodes = adjMatrix.size();
result.nodesInTopoOrder.reserve(numNodes);
Expand All @@ -2305,6 +2315,79 @@ namespace CXXGRAPH
}
}

template <typename T>
TopoSortResult<T> Graph<T>::kahn() const
{
TopoSortResult<T> result;

if (!isDirectedGraph())
{
result.errorMessage = ERR_UNDIR_GRAPH;
return result;
}
else
{
const auto adjMatrix = Graph<T>::getAdjMatrix();
const auto nodeSet = Graph<T>::getNodeSet();
result.nodesInTopoOrder.reserve(adjMatrix.size());

std::unordered_map<size_t, unsigned int> indegree;
for (const auto &node : nodeSet)
{
indegree[node->getId()] = 0;
}
for (const auto &list : adjMatrix)
{
auto children = list.second;
for (const auto &child : children)
{
indegree[std::get<0>(child)->getId()]++;
}
}

std::queue<const Node<T>*> topologicalOrder;

for (const auto &node : nodeSet)
{
if (!indegree[node->getId()])
{
topologicalOrder.emplace(node);
}
}

size_t visited = 0;
while(!topologicalOrder.empty())
{
const Node<T> *currentNode = topologicalOrder.front();
topologicalOrder.pop();
result.nodesInTopoOrder.push_back(*currentNode);

if (adjMatrix.find(currentNode) != adjMatrix.end())
{
for (const auto &child : adjMatrix.at(currentNode))
{
if (--indegree[std::get<0>(child)->getId()] == 0)
{
topologicalOrder.emplace(std::get<0>(child));
}
}
}
visited++;

}

if (visited != nodeSet.size())
{
result.errorMessage = ERR_CYCLIC_GRAPH;
result.nodesInTopoOrder.clear();
return result;
}

result.success = true;
return result;
}
}

template <typename T>
std::vector<std::vector<Node<T>>> Graph<T>::kosaraju() const
{
Expand Down
Loading

0 comments on commit 9c05b35

Please sign in to comment.