Skip to content

Commit fd90155

Browse files
authored
Floyd Warshall Algorithm (#72)
* result struct for floyd-warshall * floyd warshall implementation * floyd-warshall test cases
1 parent 0b778cd commit fd90155

File tree

4 files changed

+216
-0
lines changed

4 files changed

+216
-0
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ add_executable(test_exe test/main.cpp
5757
test/RWOutputTest.cpp
5858
test/PartitionTest.cpp
5959
test/DialTest.cpp
60+
test/FWTest.cpp
6061
)
6162
target_include_directories(test_exe PUBLIC
6263
"${PROJECT_SOURCE_DIR}/include"

include/Graph/Graph.hpp

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,14 @@ namespace CXXGRAPH
180180
*/
181181
virtual const BellmanFordResult bellmanford(const Node<T> &source, const Node<T> &target) const;
182182
/**
183+
* @brief Function runs the floyd-warshall algorithm and returns the shortest distance of
184+
* all pair of nodes. It can also detect if a negative cycle exists in the graph.
185+
* Note: No Thread Safe
186+
* @return a map whose keys are node ids and values are the shortest distance. If there is no error then also
187+
* returns if the graph contains a negative cycle.
188+
*/
189+
virtual const FWResult floydWarshall() const;
190+
/**
183191
* \brief
184192
* Function performs the breadth first search algorithm over the graph
185193
* Note: No Thread Safe
@@ -1065,6 +1073,89 @@ namespace CXXGRAPH
10651073
return result;
10661074
}
10671075

1076+
template <typename T>
1077+
const FWResult Graph<T>::floydWarshall() const
1078+
{
1079+
FWResult result;
1080+
result.success = false;
1081+
result.errorMessage = "";
1082+
std::map<std::pair<unsigned long, unsigned long>, double> pairwise_dist;
1083+
auto nodeSet = getNodeSet();
1084+
const AdjacencyMatrix<T> adj = getAdjMatrix();
1085+
// n denotes the number of vertices in graph
1086+
auto n = nodeSet.size();
1087+
// create a pairwise distance matrix with distance node distances
1088+
// set to inf. Distance of node to itself is set as 0.
1089+
for (auto elem1 : nodeSet)
1090+
{
1091+
for (auto elem2 : nodeSet)
1092+
{
1093+
auto key = std::make_pair(elem1->getId(), elem2->getId());
1094+
if (elem1 != elem2)
1095+
pairwise_dist[key] = INF_DOUBLE;
1096+
else
1097+
pairwise_dist[key] = 0.0;
1098+
}
1099+
}
1100+
1101+
auto edgeSet = this->getEdgeSet();
1102+
// update the weights of nodes
1103+
// connected by edges
1104+
for (auto edge : edgeSet)
1105+
{
1106+
auto elem = edge->getNodePair();
1107+
if (edge->isWeighted().has_value() && edge->isWeighted().value())
1108+
{
1109+
auto edgeWeight = (dynamic_cast<const Weighted *>(edge))->getWeight();
1110+
auto key = std::make_pair(elem.first->getId(), elem.second->getId());
1111+
pairwise_dist[key] = edgeWeight;
1112+
}
1113+
else
1114+
{
1115+
// if an edge exists but has no weight associated
1116+
// with it, we return an error message
1117+
result.errorMessage = ERR_NO_WEIGHTED_EDGE;
1118+
return result;
1119+
}
1120+
}
1121+
1122+
for (auto k : nodeSet)
1123+
{
1124+
// set all vertices as source one by one
1125+
for (auto src : nodeSet)
1126+
{
1127+
// iterate through all vertices as destination for the
1128+
// current source
1129+
auto src_k = std::make_pair(src->getId(), k->getId());
1130+
for (auto dst : nodeSet)
1131+
{
1132+
// If vertex k provides a shorter path than
1133+
// src to dst, update the value of
1134+
// pairwise_dist[src_to_dst]
1135+
auto src_dst = std::make_pair(src->getId(), dst->getId());
1136+
auto k_dst = std::make_pair(k->getId(), dst->getId());
1137+
if (pairwise_dist[src_dst] > (pairwise_dist[src_k] + pairwise_dist[k_dst]) && (pairwise_dist[k_dst] != INF_DOUBLE && pairwise_dist[src_k] != INF_DOUBLE))
1138+
pairwise_dist[src_dst] = pairwise_dist[src_k] + pairwise_dist[k_dst];
1139+
}
1140+
}
1141+
}
1142+
1143+
result.success = true;
1144+
// presense of negative number in the diagonal indicates
1145+
// that that the graph contains a negative cycle
1146+
for (auto node : nodeSet)
1147+
{
1148+
auto diag = std::make_pair(node->getId(), node->getId());
1149+
if (pairwise_dist[diag] < 0.)
1150+
{
1151+
result.negativeCycle = true;
1152+
return result;
1153+
}
1154+
}
1155+
result.result = std::move(pairwise_dist);
1156+
return result;
1157+
}
1158+
10681159
template <typename T>
10691160
const std::vector<Node<T>> Graph<T>::breadth_first_search(const Node<T> &start) const
10701161
{

include/Utility/Typedef.hpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,16 @@ namespace CXXGRAPH
7676
};
7777
typedef BellmanFordResult_struct BellmanFordResult;
7878

79+
/// Struct that contains the information about Floyd-Warshall Algorithm results
80+
struct FWResult_struct
81+
{
82+
bool success; // TRUE if the function does not return error, FALSE otherwise
83+
bool negativeCycle; // TRUE if graph contains a negative cycle, FALSE otherwise
84+
std::string errorMessage; //message of error
85+
std::map<std::pair<unsigned long, unsigned long>, double> result;
86+
};
87+
typedef FWResult_struct FWResult;
88+
7989
/// Struct that contains the information about Dijsktra's Algorithm results
8090
struct DialResult_struct
8191
{

test/FWTest.cpp

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
#include "gtest/gtest.h"
2+
#include "CXXGraph.hpp"
3+
#include <map>
4+
5+
// https://en.wikipedia.org/wiki/Floyd%E2%80%93Warshall_algorithm#Example
6+
TEST(FWTest, test_1)
7+
{
8+
CXXGRAPH::Node<int> node1(1, 1);
9+
CXXGRAPH::Node<int> node2(2, 2);
10+
CXXGRAPH::Node<int> node3(3, 3);
11+
CXXGRAPH::Node<int> node4(4, 4);
12+
13+
CXXGRAPH::DirectedWeightedEdge<int> edge1(1, node2, node1, 4);
14+
CXXGRAPH::DirectedWeightedEdge<int> edge2(2, node1, node3, -2);
15+
CXXGRAPH::DirectedWeightedEdge<int> edge3(3, node3, node4, 2);
16+
CXXGRAPH::DirectedWeightedEdge<int> edge4(4, node4, node2, -1);
17+
CXXGRAPH::DirectedWeightedEdge<int> edge5(3, node2, node3, 3);
18+
19+
std::list<const CXXGRAPH::Edge<int> *> edgeSet;
20+
edgeSet.push_back(&edge1);
21+
edgeSet.push_back(&edge2);
22+
edgeSet.push_back(&edge3);
23+
edgeSet.push_back(&edge4);
24+
edgeSet.push_back(&edge5);
25+
26+
CXXGRAPH::Graph<int> graph(edgeSet);
27+
CXXGRAPH::FWResult res = graph.floydWarshall();
28+
29+
std::map<std::pair<unsigned long, unsigned long>, double> pairwise_dist;
30+
auto key = std::make_pair(node1.getId(), node1.getId());
31+
auto nodeSet = graph.getNodeSet();
32+
double values[4][4] = {{0, -1, -2, 0}, {4, 0, 2, 4}, {5, 1, 0, 2}, {3, -1, 1, 0}};
33+
int row(0), col(0);
34+
for (auto elem1 : nodeSet)
35+
{
36+
col = 0;
37+
for (auto elem2 : nodeSet)
38+
{
39+
auto key = std::make_pair(elem1->getId(), elem2->getId());
40+
pairwise_dist[key] = values[row][col];
41+
col += 1;
42+
}
43+
row += 1;
44+
}
45+
auto isEqual = pairwise_dist.size() == res.result.size() && std::equal(pairwise_dist.begin(), pairwise_dist.end(), res.result.begin(),
46+
[](auto a, auto b)
47+
{ return a.first == b.first; });
48+
49+
ASSERT_TRUE(res.success);
50+
ASSERT_FALSE(res.negativeCycle);
51+
ASSERT_TRUE(isEqual);
52+
ASSERT_EQ(res.errorMessage, "");
53+
}
54+
55+
// a graph with negative cycle
56+
TEST(FWTest, test_2)
57+
{
58+
CXXGRAPH::Node<int> node0(0, 0);
59+
CXXGRAPH::Node<int> node1(1, 1);
60+
CXXGRAPH::Node<int> node2(2, 2);
61+
CXXGRAPH::DirectedWeightedEdge<int> edge1(1, node0, node1, 2);
62+
CXXGRAPH::DirectedWeightedEdge<int> edge2(2, node1, node2, 3);
63+
CXXGRAPH::DirectedWeightedEdge<int> edge3(3, node2, node0, -7);
64+
std::list<const CXXGRAPH::Edge<int> *> edgeSet;
65+
edgeSet.push_back(&edge1);
66+
edgeSet.push_back(&edge2);
67+
edgeSet.push_back(&edge3);
68+
CXXGRAPH::Graph<int> graph(edgeSet);
69+
CXXGRAPH::FWResult res = graph.floydWarshall();
70+
ASSERT_TRUE(res.success);
71+
ASSERT_TRUE(res.negativeCycle);
72+
ASSERT_EQ(res.errorMessage, "");
73+
}
74+
75+
// UndirectedWeightedEdge
76+
TEST(FWTest, test_3)
77+
{
78+
CXXGRAPH::Node<int> node1(1, 1);
79+
CXXGRAPH::Node<int> node2(2, 2);
80+
CXXGRAPH::Node<int> node3(3, 3);
81+
std::pair<const CXXGRAPH::Node<int> *, const CXXGRAPH::Node<int> *> pairNode(&node1, &node2);
82+
CXXGRAPH::DirectedWeightedEdge<int> edge1(1, pairNode, 1);
83+
CXXGRAPH::DirectedWeightedEdge<int> edge2(2, node2, node3, 1);
84+
CXXGRAPH::UndirectedWeightedEdge<int> edge3(3, node1, node3, 6);
85+
std::list<const CXXGRAPH::Edge<int> *> edgeSet;
86+
edgeSet.push_back(&edge1);
87+
edgeSet.push_back(&edge2);
88+
edgeSet.push_back(&edge3);
89+
CXXGRAPH::Graph<int> graph(edgeSet);
90+
CXXGRAPH::FWResult res = graph.floydWarshall();
91+
ASSERT_TRUE(res.success);
92+
ASSERT_FALSE(res.negativeCycle);
93+
ASSERT_EQ(res.errorMessage, "");
94+
}
95+
96+
// No weighted edge
97+
TEST(FWTest, test_4)
98+
{
99+
CXXGRAPH::Node<int> node1(1, 1);
100+
CXXGRAPH::Node<int> node2(2, 2);
101+
CXXGRAPH::Node<int> node3(3, 3);
102+
std::pair<const CXXGRAPH::Node<int> *, const CXXGRAPH::Node<int> *> pairNode(&node1, &node2);
103+
CXXGRAPH::DirectedWeightedEdge<int> edge1(1, pairNode, 1);
104+
CXXGRAPH::DirectedWeightedEdge<int> edge2(2, node2, node3, 1);
105+
CXXGRAPH::DirectedEdge<int> edge3(3, node1, node3);
106+
std::list<const CXXGRAPH::Edge<int> *> edgeSet;
107+
edgeSet.push_back(&edge1);
108+
edgeSet.push_back(&edge2);
109+
edgeSet.push_back(&edge3);
110+
CXXGRAPH::Graph<int> graph(edgeSet);
111+
CXXGRAPH::FWResult res = graph.floydWarshall();
112+
ASSERT_FALSE(res.success);
113+
ASSERT_EQ(res.errorMessage, CXXGRAPH::ERR_NO_WEIGHTED_EDGE);
114+
}

0 commit comments

Comments
 (0)