Skip to content
Snippets Groups Projects
Commit 5cd6cfc0 authored by David Rosca's avatar David Rosca
Browse files

Graph shortestpath algo: Add Floyd-Warshall algorithm + tests

parent 83010cfb
No related branches found
No related tags found
No related merge requests found
#include "FloydWarshall.h"
#include <exception/AlibException.h>
#include <limits>
namespace graph
{
namespace shortestpath
{
static void init_distances(const DirectedGraph &g, FloydWarshall::Result &d)
{
for (const DirectedEdge &e : g.getEdges()) {
int &val = d[e.getFromNode()][e.getToNode()];
val = std::min(val, g.getEdgeValue(e));
}
}
static void init_distances(const UndirectedGraph &g, FloydWarshall::Result &d)
{
for (const UndirectedEdge &e : g.getEdges()) {
int &val = d[e.getFirstNode()][e.getSecondNode()];
val = std::min(val, g.getEdgeValue(e));
d[e.getSecondNode()][e.getFirstNode()] = val;
}
}
template <typename T>
static FloydWarshall::Result floydwarshall_impl(const T &graph)
{
FloydWarshall::Result d;
const std::set<Node> &nodes = graph.getNodes();
const int inf = std::numeric_limits<int>::max();
for (const Node &n : nodes)
for (const Node &m : nodes)
d[n][m] = inf;
for (const Node &n : nodes)
d[n][n] = 0;
init_distances(graph, d);
for (const Node &k : nodes)
for (const Node &i : nodes)
if (d[i][k] != inf)
for (const Node &j : nodes)
if (d[k][j] != inf)
d[i][j] = std::min(d[i][j], d[i][k] + d[k][j]);
for (const Node &n : nodes)
if (d[n][n] < 0)
throw exception::AlibException("FloydWarshall: Detected negative-weight cycle");
return d;
}
FloydWarshall::Result FloydWarshall::floydwarshall(const Graph &graph)
{
Result res;
graph.getData().Accept(static_cast<void*>(&res), FLOYD_WARSHALL);
return res;
}
FloydWarshall::Result FloydWarshall::floydwarshall(const DirectedGraph &graph)
{
return floydwarshall_impl(graph);
}
FloydWarshall::Result FloydWarshall::floydwarshall(const UndirectedGraph &graph)
{
return floydwarshall_impl(graph);
}
void FloydWarshall::Visit(void *data, const DirectedGraph &graph) const
{
Result *r = static_cast<Result*>(data);
*r = floydwarshall(graph);
}
void FloydWarshall::Visit(void *data, const UndirectedGraph &graph) const
{
Result *r = static_cast<Result*>(data);
*r = floydwarshall(graph);
}
const FloydWarshall FloydWarshall::FLOYD_WARSHALL;
} // namespace shortestpath
} // namespace graph
#ifndef GRAPH_FLOYD_WARSHALL_FORD_H_
#define GRAPH_FLOYD_WARSHALL_FORD_H_
#include <unordered_map>
#include <graph/Graph.h>
#include <graph/directed/DirectedGraph.h>
#include <graph/undirected/UndirectedGraph.h>
namespace graph
{
namespace shortestpath
{
// Floyd-Warshall only works on graphs without negative-weight cycle
// However it will detect the negative-weight cycle and throw exception
//
// note: negative-weight undirected edge = negative-weight cycle
// note: std::numeric_limits<int>::max() is used as infinity
class FloydWarshall : public graph::VisitableGraphBase::const_visitor_type
{
public:
typedef std::unordered_map<Node, std::unordered_map<Node, int>> Result;
static Result floydwarshall(const Graph &graph);
static Result floydwarshall(const DirectedGraph &graph);
static Result floydwarshall(const UndirectedGraph &graph);
private:
void Visit(void *data, const DirectedGraph &graph) const;
void Visit(void *data, const UndirectedGraph &graph) const;
static const FloydWarshall FLOYD_WARSHALL;
};
} // namespace shortestpath
} // namespace graph
#endif // GRAPH_FLOYD_WARSHALL_FORD_H_
#include "FloydWarshallTest.h"
#include "graph/shortestpath/FloydWarshall.h"
#include <exception/AlibException.h>
CPPUNIT_TEST_SUITE_REGISTRATION(GraphFloydWarshallTest);
void GraphFloydWarshallTest::testSimple()
{
// Common
graph::shortestpath::FloydWarshall::Result res;
graph::Node n1("n1");
graph::Node n2("n2");
graph::Node n3("n3");
graph::Node n4("n4");
graph::Node n5("n5");
graph::Node n6("n6");
// Directed
graph::DirectedGraph dg;
dg.addEdge(graph::DirectedEdge(n1, n2), 2);
dg.addEdge(graph::DirectedEdge(n1, n3), 2);
dg.addEdge(graph::DirectedEdge(n1, n4), 1);
dg.addEdge(graph::DirectedEdge(n1, n5), 6);
dg.addEdge(graph::DirectedEdge(n2, n5), 3);
dg.addEdge(graph::DirectedEdge(n5, n6), 2);
CPPUNIT_ASSERT_EQUAL(6, (int)dg.getNodes().size());
res = graph::shortestpath::FloydWarshall::floydwarshall(dg);
CPPUNIT_ASSERT_EQUAL(0, res[n1][n1]);
CPPUNIT_ASSERT_EQUAL(2, res[n1][n2]);
CPPUNIT_ASSERT_EQUAL(2, res[n1][n3]);
CPPUNIT_ASSERT_EQUAL(1, res[n1][n4]);
CPPUNIT_ASSERT_EQUAL(5, res[n1][n5]);
CPPUNIT_ASSERT_EQUAL(7, res[n1][n6]);
// Undirected
graph::UndirectedGraph ug;
ug.addEdge(graph::UndirectedEdge(n1, n2), 2);
ug.addEdge(graph::UndirectedEdge(n1, n3), 2);
ug.addEdge(graph::UndirectedEdge(n1, n4), 1);
ug.addEdge(graph::UndirectedEdge(n1, n5), 6);
ug.addEdge(graph::UndirectedEdge(n2, n5), 3);
ug.addEdge(graph::UndirectedEdge(n5, n6), 2);
res = graph::shortestpath::FloydWarshall::floydwarshall(ug);
CPPUNIT_ASSERT_EQUAL(0, res[n1][n1]);
CPPUNIT_ASSERT_EQUAL(2, res[n1][n2]);
CPPUNIT_ASSERT_EQUAL(2, res[n1][n3]);
CPPUNIT_ASSERT_EQUAL(1, res[n1][n4]);
CPPUNIT_ASSERT_EQUAL(5, res[n1][n5]);
CPPUNIT_ASSERT_EQUAL(7, res[n1][n6]);
}
void GraphFloydWarshallTest::testCycle()
{
// Common
graph::shortestpath::FloydWarshall::Result res;
graph::Node n1("n1");
graph::Node n2("n2");
graph::Node n3("n3");
graph::Node n4("n4");
graph::Node n5("n5");
graph::Node n6("n6");
// Directed
graph::DirectedGraph dg;
dg.addEdge(graph::DirectedEdge(n1, n2), 2);
dg.addEdge(graph::DirectedEdge(n1, n3), 2);
dg.addEdge(graph::DirectedEdge(n1, n4), 1);
dg.addEdge(graph::DirectedEdge(n1, n5), 6);
dg.addEdge(graph::DirectedEdge(n2, n5), 3);
dg.addEdge(graph::DirectedEdge(n5, n6), 2);
dg.addEdge(graph::DirectedEdge(n6, n1), 1);
res = graph::shortestpath::FloydWarshall::floydwarshall(dg);
CPPUNIT_ASSERT_EQUAL(0, res[n1][n1]);
CPPUNIT_ASSERT_EQUAL(2, res[n1][n2]);
CPPUNIT_ASSERT_EQUAL(2, res[n1][n3]);
CPPUNIT_ASSERT_EQUAL(1, res[n1][n4]);
CPPUNIT_ASSERT_EQUAL(5, res[n1][n5]);
CPPUNIT_ASSERT_EQUAL(7, res[n1][n6]);
// Undirected
graph::UndirectedGraph ug;
ug.addEdge(graph::UndirectedEdge(n1, n2), 2);
ug.addEdge(graph::UndirectedEdge(n1, n3), 2);
ug.addEdge(graph::UndirectedEdge(n1, n4), 1);
ug.addEdge(graph::UndirectedEdge(n1, n5), 6);
ug.addEdge(graph::UndirectedEdge(n2, n5), 3);
ug.addEdge(graph::UndirectedEdge(n5, n6), 2);
ug.addEdge(graph::UndirectedEdge(n6, n1), 1);
res = graph::shortestpath::FloydWarshall::floydwarshall(ug);
CPPUNIT_ASSERT_EQUAL(0, res[n1][n1]);
CPPUNIT_ASSERT_EQUAL(2, res[n1][n2]);
CPPUNIT_ASSERT_EQUAL(2, res[n1][n3]);
CPPUNIT_ASSERT_EQUAL(1, res[n1][n4]);
CPPUNIT_ASSERT_EQUAL(3, res[n1][n5]);
CPPUNIT_ASSERT_EQUAL(1, res[n1][n6]);
}
void GraphFloydWarshallTest::testMultiEdge()
{
// Common
graph::shortestpath::FloydWarshall::Result res;
graph::Node n1("n1");
graph::Node n2("n2");
graph::Node n3("n3");
graph::Node n4("n4");
graph::Node n5("n5");
graph::Node n6("n6");
// Directed
graph::DirectedGraph dg;
dg.addEdge(graph::DirectedEdge(n1, n2), 2);
dg.addEdge(graph::DirectedEdge(n1, n2, "multi"), 1);
dg.addEdge(graph::DirectedEdge(n1, n3), 2);
dg.addEdge(graph::DirectedEdge(n1, n4), 1);
dg.addEdge(graph::DirectedEdge(n1, n5), 6);
dg.addEdge(graph::DirectedEdge(n2, n5), 3);
dg.addEdge(graph::DirectedEdge(n5, n6), 2);
dg.addEdge(graph::DirectedEdge(n6, n1), 1);
res = graph::shortestpath::FloydWarshall::floydwarshall(dg);
CPPUNIT_ASSERT_EQUAL(0, res[n1][n1]);
CPPUNIT_ASSERT_EQUAL(1, res[n1][n2]);
CPPUNIT_ASSERT_EQUAL(2, res[n1][n3]);
CPPUNIT_ASSERT_EQUAL(1, res[n1][n4]);
CPPUNIT_ASSERT_EQUAL(4, res[n1][n5]);
CPPUNIT_ASSERT_EQUAL(6, res[n1][n6]);
// Undirected
graph::UndirectedGraph ug;
ug.addEdge(graph::UndirectedEdge(n1, n2), 2);
ug.addEdge(graph::UndirectedEdge(n1, n2, "multi"), 1);
ug.addEdge(graph::UndirectedEdge(n1, n3), 2);
ug.addEdge(graph::UndirectedEdge(n1, n4), 1);
ug.addEdge(graph::UndirectedEdge(n1, n5), 6);
ug.addEdge(graph::UndirectedEdge(n2, n5), 3);
ug.addEdge(graph::UndirectedEdge(n5, n6), 2);
res = graph::shortestpath::FloydWarshall::floydwarshall(ug);
CPPUNIT_ASSERT_EQUAL(0, res[n1][n1]);
CPPUNIT_ASSERT_EQUAL(1, res[n1][n2]);
CPPUNIT_ASSERT_EQUAL(2, res[n1][n3]);
CPPUNIT_ASSERT_EQUAL(1, res[n1][n4]);
CPPUNIT_ASSERT_EQUAL(4, res[n1][n5]);
CPPUNIT_ASSERT_EQUAL(6, res[n1][n6]);
}
void GraphFloydWarshallTest::testNegativeValue()
{
// Common
graph::shortestpath::FloydWarshall::Result res;
graph::Node n1("n1");
graph::Node n2("n2");
graph::Node n3("n3");
graph::Node n4("n4");
graph::Node n5("n5");
graph::Node n6("n6");
// Directed
graph::DirectedGraph dg;
dg.addEdge(graph::DirectedEdge(n1, n2), 5);
dg.addEdge(graph::DirectedEdge(n1, n3), 1);
dg.addEdge(graph::DirectedEdge(n1, n4), 2);
dg.addEdge(graph::DirectedEdge(n1, n5), 5);
dg.addEdge(graph::DirectedEdge(n2, n5), -1);
dg.addEdge(graph::DirectedEdge(n5, n6), -2);
res = graph::shortestpath::FloydWarshall::floydwarshall(dg);
CPPUNIT_ASSERT_EQUAL(0, res[n1][n1]);
CPPUNIT_ASSERT_EQUAL(5, res[n1][n2]);
CPPUNIT_ASSERT_EQUAL(1, res[n1][n3]);
CPPUNIT_ASSERT_EQUAL(2, res[n1][n4]);
CPPUNIT_ASSERT_EQUAL(4, res[n1][n5]);
CPPUNIT_ASSERT_EQUAL(2, res[n1][n6]);
}
void GraphFloydWarshallTest::testNegativeCycle()
{
// Common
bool exception;
graph::Node n1("n1");
graph::Node n2("n2");
graph::Node n3("n3");
graph::Node n4("n4");
graph::Node n5("n5");
graph::Node n6("n6");
// Directed
graph::DirectedGraph dg;
dg.addEdge(graph::DirectedEdge(n1, n2), 5);
dg.addEdge(graph::DirectedEdge(n1, n3), 1);
dg.addEdge(graph::DirectedEdge(n1, n4), 2);
dg.addEdge(graph::DirectedEdge(n1, n5), 5);
dg.addEdge(graph::DirectedEdge(n2, n5), -1);
dg.addEdge(graph::DirectedEdge(n5, n6), -2);
dg.addEdge(graph::DirectedEdge(n6, n1), -3);
exception = false;
try {
graph::shortestpath::FloydWarshall::floydwarshall(dg);
} catch (exception::AlibException) {
exception = true;
}
CPPUNIT_ASSERT_EQUAL(true, exception);
// Undirected
graph::UndirectedGraph ug;
ug.addEdge(graph::UndirectedEdge(n1, n2), 5);
ug.addEdge(graph::UndirectedEdge(n1, n3), 1);
ug.addEdge(graph::UndirectedEdge(n1, n4), 2);
ug.addEdge(graph::UndirectedEdge(n2, n5), -1);
exception = false;
try {
graph::shortestpath::FloydWarshall::floydwarshall(ug);
} catch (exception::AlibException) {
exception = true;
}
CPPUNIT_ASSERT_EQUAL(true, exception);
}
#ifndef FLOYD_WARSHALL_TEST_H_
#define FLOYD_WARSHALL_TEST_H_
#include <cppunit/extensions/HelperMacros.h>
class GraphFloydWarshallTest : public CppUnit::TestFixture
{
CPPUNIT_TEST_SUITE(GraphFloydWarshallTest);
CPPUNIT_TEST(testSimple);
CPPUNIT_TEST(testCycle);
CPPUNIT_TEST(testMultiEdge);
CPPUNIT_TEST(testNegativeValue);
CPPUNIT_TEST(testNegativeCycle);
CPPUNIT_TEST_SUITE_END();
public:
void testSimple();
void testCycle();
void testMultiEdge();
void testNegativeValue();
void testNegativeCycle();
};
#endif // FLOYD_WARSHALL_TEST_H_
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment