Skip to content

Commit

Permalink
removed redundant negative cicle check and according tests, added two…
Browse files Browse the repository at this point in the history
… new tests
  • Loading branch information
Hromz committed Sep 27, 2023
1 parent 7a1ca3c commit 4a43bce
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 103 deletions.
14 changes: 8 additions & 6 deletions docs/docs/algorithms/shortest-path/floyd-warshall.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@ sidebar_position: 5
---

# Floyd-Warshall algorithm

Floyd-Warshall algorithm computes the shortest path between any two vertices in a graph, both directed and undirected.
The key idea of the algorithm is to relax the weighted shortest path between any two vertices, using any vertex as an intermediate one.
Advantage of the algorithm is that it processes vertices instead of edges. This advantage can be used when the number of edges is large enough, aka a dense graph.
The algorithm does not work for graphs with negative weight cycles.
The key idea of the algorithm is to relax the weighted shortest path between any two vertices, using any vertex as an
intermediate one.
Advantage of the algorithm is that it processes vertices instead of edges. This advantage can be used when the number of
edges is large enough, aka a dense graph.
Runtime of the algorithm is O(|V<sup>3</sup>|) and memory consumption is O(|V<sup>2</sup>|).

[wikipedia](https://en.wikipedia.org/wiki/Floyd%E2%80%93Warshall_algorithm)
Expand All @@ -22,7 +26,5 @@ std::vector<std::vector<WEIGHT_T>> floyd_warshall_shortest_paths(
```
- **graph** The graph to extract the shortest path from.
- **return** Returns a 2D vector of the shortest path. If a path doesn't exist between two vertices, mark it as TYPE_MAX.
### Special case
In the case of a negative cycle in both directed and undirected graphs, the algorithm marks vertices as TYPE_MIN.
- **return** Returns a 2D vector of the shortest path. If a path doesn't exist between two vertices, mark it as
TYPE_MAX.
26 changes: 0 additions & 26 deletions include/graaflib/algorithm/shortest_path/floyd_warshall.tpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,36 +7,12 @@

namespace graaf::algorithm {

namespace detail {

// Mark vertices in negative cycle as NO_PATH
template <typename WEIGHT_T>
void do_fw_detect_negative_cycle(std::vector<std::vector<WEIGHT_T>>& distance,
WEIGHT_T ZERO, WEIGHT_T INF, WEIGHT_T NO_PATH,
size_t n) {
for (std::size_t start_vertex = 0; start_vertex < n; ++start_vertex) {
for (std::size_t end_vertex = 0; end_vertex < n; ++end_vertex) {
for (std::size_t through_vertex = 0; through_vertex < n;
++through_vertex) {
if (distance[start_vertex][through_vertex] < INF &&
distance[through_vertex][through_vertex] < ZERO &&
distance[through_vertex][end_vertex] < INF) {
distance[start_vertex][end_vertex] = NO_PATH;
}
}
}
}
}

}; // namespace detail

template <typename V, typename E, graph_type T, typename WEIGHT_T>
std::vector<std::vector<WEIGHT_T>> floyd_warshall_shortest_paths(
const graph<V, E, T>& graph) {
WEIGHT_T ZERO{};
std::size_t n = graph.vertex_count();
auto INF = std::numeric_limits<WEIGHT_T>::max();
auto NO_PATH = std::numeric_limits<WEIGHT_T>::min();

std::vector<std::vector<WEIGHT_T>> shortest_paths(
n, std::vector<WEIGHT_T>(n, INF));
Expand Down Expand Up @@ -69,8 +45,6 @@ std::vector<std::vector<WEIGHT_T>> floyd_warshall_shortest_paths(
}
}

// Mark vertices in negative cycles with INF
detail::do_fw_detect_negative_cycle(shortest_paths, ZERO, INF, NO_PATH, n);
return shortest_paths;
}

Check warning on line 49 in include/graaflib/algorithm/shortest_path/floyd_warshall.tpp

View check run for this annotation

Codecov / codecov/patch

include/graaflib/algorithm/shortest_path/floyd_warshall.tpp#L49

Added line #L49 was not covered by tests

Expand Down
140 changes: 69 additions & 71 deletions test/graaflib/algorithm/floyd_warshall_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,64 +112,6 @@ TYPED_TEST(FloydWarshallTest, DirectedGraph) {
ASSERT_EQ(shortest_paths, expected_paths);
}

TYPED_TEST(FloydWarshallTest, DirectedGraphNegativeCycle) {
// GIVEN
directed_graph<int, int> graph{};

// graph vertices
const auto vertex_1{graph.add_vertex(10)};
const auto vertex_2{graph.add_vertex(20)};
const auto vertex_3{graph.add_vertex(30)};
const auto vertex_4{graph.add_vertex(40)};

// adding edges to our graph
graph.add_edge(vertex_1, vertex_2, -100);
graph.add_edge(vertex_2, vertex_3, 300);
graph.add_edge(vertex_3, vertex_1, -300);
graph.add_edge(vertex_4, vertex_2, 300);

auto NEG_CYCLE = INT_MIN;
auto NO_PATH = INT_MAX;

auto shortest_paths = floyd_warshall_shortest_paths(graph);
std::vector<std::vector<int>> expected_paths = {
{NEG_CYCLE, NEG_CYCLE, NEG_CYCLE, NO_PATH},
{NEG_CYCLE, NEG_CYCLE, NEG_CYCLE, NO_PATH},
{NEG_CYCLE, NEG_CYCLE, NEG_CYCLE, NO_PATH},
{NEG_CYCLE, NEG_CYCLE, NEG_CYCLE, 0}};

ASSERT_EQ(shortest_paths, expected_paths);
}

TYPED_TEST(FloydWarshallTest, UndirectedGraphNegativeCycle) {
// GIVEN
undirected_graph<int, int> graph{};

// graph vertices
const auto vertex_1{graph.add_vertex(10)};
const auto vertex_2{graph.add_vertex(20)};
const auto vertex_3{graph.add_vertex(30)};
const auto vertex_4{graph.add_vertex(40)};

// adding edges to our graph
graph.add_edge(vertex_1, vertex_2, -100);
graph.add_edge(vertex_2, vertex_3, 300);
graph.add_edge(vertex_3, vertex_1, -300);
graph.add_edge(vertex_4, vertex_2, 300);

auto NEG_CYCLE = INT_MIN;
auto NO_PATH = INT_MAX;

auto shortest_paths = floyd_warshall_shortest_paths(graph);
std::vector<std::vector<int>> expected_paths = {
{NEG_CYCLE, NEG_CYCLE, NEG_CYCLE, NEG_CYCLE},
{NEG_CYCLE, NEG_CYCLE, NEG_CYCLE, NEG_CYCLE},
{NEG_CYCLE, NEG_CYCLE, NEG_CYCLE, NEG_CYCLE},
{NEG_CYCLE, NEG_CYCLE, NEG_CYCLE, NEG_CYCLE}};

ASSERT_EQ(shortest_paths, expected_paths);
}

TYPED_TEST(FloydWarshallTest, DirectedGraphNoCycleNegativeWeight) {
directed_graph<int, int> graph{};

Expand All @@ -189,7 +131,6 @@ TYPED_TEST(FloydWarshallTest, DirectedGraphNoCycleNegativeWeight) {
graph.add_edge(vertex_4, vertex_6, 100);
graph.add_edge(vertex_5, vertex_2, -75);

auto NEG_CYCLE = INT_MIN;
auto NO_PATH = INT_MAX;

auto shortest_paths = floyd_warshall_shortest_paths(graph);
Expand Down Expand Up @@ -254,31 +195,88 @@ TYPED_TEST(FloydWarshallTest, DenseUndirectedGraph) {
ASSERT_EQ(shortest_paths, expected_paths);
}

TYPED_TEST(FloydWarshallTest, DirectedGraphWithSubgraphNegativeCycle) {
directed_graph<int, int> graph{};
TYPED_TEST(FloydWarshallTest, UndirectedGraphTwoComponents) {
undirected_graph<int, int> graph{};

// graph vertices
const auto vertex_1{graph.add_vertex(10)};
const auto vertex_2{graph.add_vertex(20)};
const auto vertex_3{graph.add_vertex(30)};
const auto vertex_4{graph.add_vertex(40)};
const auto vertex_5{graph.add_vertex(40)};
const auto vertex_6{graph.add_vertex(40)};
const auto vertex_7{graph.add_vertex(40)};
const auto vertex_8{graph.add_vertex(40)};

// adding edges to our graph
graph.add_edge(vertex_1, vertex_2, 10);
graph.add_edge(vertex_2, vertex_1, -20);
graph.add_edge(vertex_1, vertex_2, 12);
graph.add_edge(vertex_1, vertex_3, 11);
graph.add_edge(vertex_1, vertex_5, 6);
graph.add_edge(vertex_2, vertex_5, 14);
graph.add_edge(vertex_2, vertex_4, 15);
graph.add_edge(vertex_4, vertex_5, 9);
graph.add_edge(vertex_4, vertex_3, 7);
graph.add_edge(vertex_5, vertex_3, 7);

graph.add_edge(vertex_6, vertex_7, 4);
graph.add_edge(vertex_6, vertex_8, 2);
graph.add_edge(vertex_7, vertex_8, 3);

graph.add_edge(vertex_4, vertex_3, 75);
graph.add_edge(vertex_3, vertex_4, 12);
auto NO_PATH = INT_MAX;

auto shortest_paths = floyd_warshall_shortest_paths(graph);
std::vector<std::vector<int>> expected_paths{
{0, 12, 11, 15, 6, NO_PATH, NO_PATH, NO_PATH},
{12, 0, 21, 15, 14, NO_PATH, NO_PATH, NO_PATH},
{11, 21, 0, 7, 7, NO_PATH, NO_PATH, NO_PATH},
{15, 15, 7, 0, 9, NO_PATH, NO_PATH, NO_PATH},
{6, 14, 7, 9, 0, NO_PATH, NO_PATH, NO_PATH},
{NO_PATH, NO_PATH, NO_PATH, NO_PATH, NO_PATH, 0, 4, 2},
{NO_PATH, NO_PATH, NO_PATH, NO_PATH, NO_PATH, 4, 0, 3},
{NO_PATH, NO_PATH, NO_PATH, NO_PATH, NO_PATH, 2, 3, 0}};

ASSERT_EQ(shortest_paths, expected_paths);
}

TYPED_TEST(FloydWarshallTest, DirectedGraphTwoComponents) {
undirected_graph<int, int> graph{};

// graph vertices
const auto vertex_1{graph.add_vertex(10)};
const auto vertex_2{graph.add_vertex(20)};
const auto vertex_3{graph.add_vertex(30)};
const auto vertex_4{graph.add_vertex(40)};
const auto vertex_5{graph.add_vertex(40)};
const auto vertex_6{graph.add_vertex(40)};
const auto vertex_7{graph.add_vertex(40)};
const auto vertex_8{graph.add_vertex(40)};

// adding edges to our graph
graph.add_edge(vertex_1, vertex_2, 12);
graph.add_edge(vertex_1, vertex_3, 11);
graph.add_edge(vertex_5, vertex_1, 6);
graph.add_edge(vertex_2, vertex_5, 14);
graph.add_edge(vertex_2, vertex_4, 15);
graph.add_edge(vertex_5, vertex_4, 9);
graph.add_edge(vertex_4, vertex_3, 7);
graph.add_edge(vertex_3, vertex_5, 7);

graph.add_edge(vertex_6, vertex_7, 4);
graph.add_edge(vertex_6, vertex_8, 2);
graph.add_edge(vertex_8, vertex_7, 3);

auto NEG_CYCLE = INT_MIN;
auto NO_PATH = INT_MAX;

auto shortest_paths = floyd_warshall_shortest_paths(graph);
std::vector<std::vector<int>> expected_paths = {
{NEG_CYCLE, NEG_CYCLE, NO_PATH, NO_PATH},
{NEG_CYCLE, NEG_CYCLE, NO_PATH, NO_PATH},
{NO_PATH, NO_PATH, 0, 12},
{NO_PATH, NO_PATH, 75, 0}};
std::vector<std::vector<int>> expected_paths{
{0, 12, 11, 15, 6, NO_PATH, NO_PATH, NO_PATH},
{12, 0, 21, 15, 14, NO_PATH, NO_PATH, NO_PATH},
{11, 21, 0, 7, 7, NO_PATH, NO_PATH, NO_PATH},
{15, 15, 7, 0, 9, NO_PATH, NO_PATH, NO_PATH},
{6, 14, 7, 9, 0, NO_PATH, NO_PATH, NO_PATH},
{NO_PATH, NO_PATH, NO_PATH, NO_PATH, NO_PATH, 0, 4, 2},
{NO_PATH, NO_PATH, NO_PATH, NO_PATH, NO_PATH, 4, 0, 3},
{NO_PATH, NO_PATH, NO_PATH, NO_PATH, NO_PATH, 2, 3, 0}};

ASSERT_EQ(shortest_paths, expected_paths);
}
Expand Down

0 comments on commit 4a43bce

Please sign in to comment.