Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

introduce topological sort for graph #247

Merged
merged 1 commit into from
Nov 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions benchmark/TopologicalSort_BM.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#include <benchmark/benchmark.h>
#include "CXXGraph.hpp"
#include "Utilities.hpp"


static void TopologicalSort_X(benchmark::State &state)
{
CXXGRAPH::Graph<int> g;
auto range_start = edges.begin();
auto range_end = edges.find(state.range(0));
std::unordered_map<unsigned long, CXXGRAPH::Edge<int> *> edgesX;
edgesX.insert(range_start, range_end);
for (auto e : edgesX)
{
g.addEdge(&(*e.second));
}
for (auto _ : state)
{
const auto &result = g.topologicalSort();
}
}
BENCHMARK(TopologicalSort_X)->RangeMultiplier(16)->Range((unsigned long)1, (unsigned long)1 << 16);

static void TopologicalSort_FromReadedCitHep(benchmark::State &state)
{
auto edgeSet = cit_graph_ptr->getEdgeSet();
for (auto _ : state)
{
const auto &result = cit_graph_ptr->topologicalSort();
}
}

BENCHMARK(TopologicalSort_FromReadedCitHep);
66 changes: 66 additions & 0 deletions include/Graph/Graph.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,14 @@ namespace CXXGRAPH
*/
virtual bool isStronglyConnectedGraph() const;

/**
* @brief This function sort nodes in topological order.
* Applicable for Directed Acyclic Graph
*
* @return a vector containing nodes in topological order
*/
virtual TopoSortResult<T> topologicalSort() const;

/**
* \brief
* This function performs performs the kosaraju algorthm on the graph to find the strongly connected components.
Expand Down Expand Up @@ -1971,6 +1979,64 @@ namespace CXXGRAPH
}
}

template <typename T>
TopoSortResult<T> Graph<T>::topologicalSort() const
{
TopoSortResult<T> result;
result.success = false;

if (!isDirectedGraph())
{
result.errorMessage = ERR_UNDIR_GRAPH;
return result;
}
else if (isCyclicDirectedGraphBFS())
{
result.errorMessage = ERR_CYCLIC_GRAPH;
return result;
}
else
{
const auto &adjMatrix = getAdjMatrix();
const auto &nodeSet = getNodeSet();
std::unordered_map<const Node<T> *, bool> visited;

std::function<void(const Node<T> *)> postorder_helper = [&postorder_helper, &adjMatrix, &visited, &result] (const Node<T> *curNode)
{
visited[curNode] = true;

if (adjMatrix.find(curNode) != adjMatrix.end())
{
for (const auto &edge : adjMatrix.at(curNode))
{
const auto &nextNode = edge.first;
if (false == visited[nextNode])
{
postorder_helper(nextNode);
}
}
}

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

int numNodes = adjMatrix.size();
result.nodesInTopoOrder.reserve(numNodes);

for (const auto &node : nodeSet)
{
if (false == visited[node])
{
postorder_helper(node);
}
}

result.success = true;
std::reverse(result.nodesInTopoOrder.begin(), result.nodesInTopoOrder.end());
return result;
}
}

template <typename T>
std::vector<std::vector<Node<T>>> Graph<T>::kosaraju() const
{
Expand Down
2 changes: 2 additions & 0 deletions include/Utility/ConstString.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ namespace CXXGRAPH
//STRING ERROR CONST EXPRESSION
constexpr char ERR_NO_DIR_OR_UNDIR_EDGE[] = "Edge are neither Directed neither Undirected";
constexpr char ERR_DIR_GRAPH[] = "Graph is directed";
constexpr char ERR_UNDIR_GRAPH[] = "Graph is undirected";
constexpr char ERR_CYCLIC_GRAPH[] = "Graph is cyclic";
constexpr char ERR_NO_WEIGHTED_EDGE[] = "Edge are not Weighted";
constexpr char ERR_NEGATIVE_WEIGHTED_EDGE[] = "Edge negative Weighted";
constexpr char ERR_TARGET_NODE_NOT_REACHABLE[] = "Target Node not Reachable";
Expand Down
19 changes: 19 additions & 0 deletions include/Utility/Typedef.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,25 @@ namespace CXXGRAPH
};
typedef DialResult_struct DialResult;

/// Struct that contains the information about TopologicalSort's Algorithm results
template <typename T>
struct TopoSortResult_struct
{
bool success = false; // TRUE if the function does not return error, FALSE otherwise
std::string errorMessage = ""; //message of error
std::vector<Node<T>> nodesInTopoOrder = {}; //result a vector that contains the nodes in topological order (valid only if success is TRUE)

/*
TopoSortResult & operator=(TopoSortResult_struct && res)
: success(res.success),
errorMessage(move(res.errorMessage))
nodesInTopoOrder(move(res.nodesInTopoOrder))
{}
*/
};
template <typename T>
using TopoSortResult = TopoSortResult_struct<T>;

/// Struct that contains the information about the partitioning statistics


Expand Down
105 changes: 105 additions & 0 deletions test/TopologicalSortTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#include "gtest/gtest.h"
#include "CXXGraph.hpp"
#include "Utility/Typedef.hpp"
#include "Utility/ConstString.hpp"

// topological sort test in a cyclic graph
TEST(TopologicalSortTest, test_1)
{
CXXGRAPH::Node<int> node1("1", 1);
CXXGRAPH::Node<int> node2("2", 2);
CXXGRAPH::Node<int> node3("3", 3);
std::pair<const CXXGRAPH::Node<int> *, const CXXGRAPH::Node<int> *> pairNode(&node1, &node2);
CXXGRAPH::DirectedWeightedEdge<int> edge1(1, pairNode, 1);
CXXGRAPH::DirectedWeightedEdge<int> edge2(2, node2, node3, 2);
CXXGRAPH::DirectedWeightedEdge<int> edge3(3, node3, node1, 3);
CXXGRAPH::T_EdgeSet<int> edgeSet;
edgeSet.insert(&edge1);
edgeSet.insert(&edge2);
edgeSet.insert(&edge3);
CXXGRAPH::Graph<int> graph(edgeSet);

CXXGRAPH::TopoSortResult<int> && res = graph.topologicalSort();
ASSERT_EQ(res.success, false);
ASSERT_EQ(res.errorMessage, CXXGRAPH::ERR_CYCLIC_GRAPH);
}

// topogical sort test in a undirected graph
TEST(TopologicalSortTest, test_2)
{
CXXGRAPH::Node<int> node1("1", 1);
CXXGRAPH::Node<int> node2("2", 2);
CXXGRAPH::Node<int> node3("3", 3);
CXXGRAPH::Node<int> node4("4", 4);
CXXGRAPH::Node<int> node5("5", 5);
std::pair<const CXXGRAPH::Node<int> *, const CXXGRAPH::Node<int> *> pairNode(&node1, &node2);
CXXGRAPH::UndirectedWeightedEdge<int> edge1(1, pairNode, 1);
CXXGRAPH::UndirectedWeightedEdge<int> edge2(2, node2, node3, 1);
CXXGRAPH::UndirectedWeightedEdge<int> edge3(3, node3, node4, 1);
CXXGRAPH::UndirectedWeightedEdge<int> edge4(3, node3, node5, 1);
CXXGRAPH::T_EdgeSet<int> edgeSet;
edgeSet.insert(&edge1);
edgeSet.insert(&edge2);
edgeSet.insert(&edge3);
edgeSet.insert(&edge4);
CXXGRAPH::Graph<int> graph(edgeSet);

CXXGRAPH::TopoSortResult<int> && res = graph.topologicalSort();
ASSERT_EQ(res.success, false);
ASSERT_EQ(res.errorMessage, CXXGRAPH::ERR_UNDIR_GRAPH);
}

// topogical sort test in a tiny graph
TEST(TopologicalSortTest, test_3)
{
CXXGRAPH::Node<int> node1("1", 1);
CXXGRAPH::Node<int> node2("2", 2);
CXXGRAPH::Node<int> node3("3", 3);
CXXGRAPH::Node<int> node4("4", 4);
CXXGRAPH::Node<int> node5("5", 5);
CXXGRAPH::Node<int> node6("6", 6);
CXXGRAPH::Node<int> node7("7", 7);
CXXGRAPH::Node<int> node8("8", 8);
std::pair<const CXXGRAPH::Node<int> *, const CXXGRAPH::Node<int> *> pairNode(&node1, &node2);
CXXGRAPH::DirectedWeightedEdge<int> edge1(1, pairNode, 1);
CXXGRAPH::DirectedWeightedEdge<int> edge2(2, node2, node3, 1);
CXXGRAPH::DirectedWeightedEdge<int> edge3(3, node2, node6, 6);
CXXGRAPH::DirectedWeightedEdge<int> edge4(3, node3, node4, 6);
CXXGRAPH::DirectedWeightedEdge<int> edge5(3, node3, node5, 6);
CXXGRAPH::DirectedWeightedEdge<int> edge6(3, node6, node7, 6);
CXXGRAPH::DirectedWeightedEdge<int> edge7(3, node8, node6, 6);
CXXGRAPH::DirectedWeightedEdge<int> edge8(3, node8, node7, 6);
CXXGRAPH::T_EdgeSet<int> edgeSet;
edgeSet.insert(&edge1);
edgeSet.insert(&edge2);
edgeSet.insert(&edge3);
edgeSet.insert(&edge4);
edgeSet.insert(&edge5);
edgeSet.insert(&edge6);
edgeSet.insert(&edge7);
edgeSet.insert(&edge8);
CXXGRAPH::Graph<int> graph(edgeSet);

CXXGRAPH::TopoSortResult<int> && res = graph.topologicalSort();
ASSERT_EQ(res.success, true);
ASSERT_TRUE(res.errorMessage.empty());
ASSERT_EQ(res.nodesInTopoOrder.size(), 8);

// check topological order of nodes
std::unordered_map<unsigned long, int> nodeToOrder;
for (int i = 0; i < res.nodesInTopoOrder.size(); ++i)
{
nodeToOrder[res.nodesInTopoOrder[i].getId()] = i;
}

for (const auto & edge : edgeSet)
{
auto node_id1 = edge->getNodePair().first->getId();
auto node_id2 = edge->getNodePair().second->getId();

ASSERT_TRUE(nodeToOrder.count(node_id1) && nodeToOrder.count(node_id2));
ASSERT_LT(nodeToOrder[node_id1], nodeToOrder[node_id2]);
}
}

// more test cases to be needed