Skip to content

Commit

Permalink
added the negative cycle detection in bellman ford
Browse files Browse the repository at this point in the history
and relevant unit tests too.
  • Loading branch information
unbalancedvariance committed Sep 21, 2023
1 parent a1e229c commit 55cab98
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 1 deletion.
11 changes: 10 additions & 1 deletion include/graaflib/algorithm/shortest_path.tpp
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,16 @@ bellman_ford_shortest_paths(const graph<V, E, T>& graph,
}
}
}

// Negative cycle detection by doing an additional pass in the graph
for (const auto& [edge_id, edge] : graph.get_edges()) {
const auto [u, v]{edge_id};
WEIGHT_T weight = get_weight(edge);
if (shortest_paths[u].total_weight != std::numeric_limits<WEIGHT_T>::max() && shortest_paths[u].total_weight + weight < shortest_paths[v].total_weight) {
std::ostringstream error_msg;
error_msg << "Negative cycle detected in the graph.";
throw std::invalid_argument{error_msg.str()};
}
}
return shortest_paths;
}

Expand Down
60 changes: 60 additions & 0 deletions test/graaflib/algorithm/shortest_path_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -626,7 +626,67 @@ TYPED_TEST(BellmanFordShortestPathsTest,
expected_path_map[vertex_id_5] = path5;
ASSERT_EQ(path_map, expected_path_map);
}
template <typename T>
struct BellmanFordShortestPathsSignedTypesTest : public testing::Test {
using graph_t = typename T::first_type;
using edge_t = typename T::second_type;
};
using undirected_weighted_graph_signed_types = testing::Types<
/**
* Primitive edge type directed graph
*/
std::pair<directed_graph<int, int>, int>,
std::pair<directed_graph<int, float>, float>,
std::pair<directed_graph<int, long double>, long double>,

/**
* Non primitive weighted edge type directed graph
*/
std::pair<directed_graph<int, my_weighted_edge<int>>,my_weighted_edge<int>>,
std::pair<directed_graph<int, my_weighted_edge<float>>,my_weighted_edge<float>>,
std::pair<directed_graph<int, my_weighted_edge<long double>>,my_weighted_edge<long double>>>;

TYPED_TEST_SUITE(BellmanFordShortestPathsSignedTypesTest,undirected_weighted_graph_signed_types);

TYPED_TEST(BellmanFordShortestPathsSignedTypesTest, BellmanFordNegativecycleTest) {
// Bellman Ford can handle negative edge weights only in directed graphs.
// GIVEN
using graph_t = typename TestFixture::graph_t;
using edge_t = typename TestFixture::edge_t;
using weight_t = decltype(get_weight(std::declval<edge_t>()));

graph_t graph{};
// Adding vertices
const auto vertex_id_1{graph.add_vertex(10)};
const auto vertex_id_2{graph.add_vertex(20)};
const auto vertex_id_3{graph.add_vertex(30)};
const auto vertex_id_4{graph.add_vertex(40)};

// Adding Edges

// Negative cycle exists between the vertices 2,3 and 4.
graph.add_edge(vertex_id_2, vertex_id_1, edge_t{static_cast<weight_t>(1)});
graph.add_edge(vertex_id_2, vertex_id_3, edge_t{static_cast<weight_t>(-6)});
graph.add_edge(vertex_id_3, vertex_id_4, edge_t{static_cast<weight_t>(-1)});
graph.add_edge(vertex_id_4, vertex_id_2, edge_t{static_cast<weight_t>(-2)});

ASSERT_THROW(
{
try {
[[maybe_unused]] const auto path{bellman_ford_shortest_paths(graph, vertex_id_3)};
// If the above line doesn't throw an exception, fail the test
FAIL()
<< "Expected std::invalid_argument exception, but no exception was thrown";
}
catch (const std::invalid_argument &ex) {
// Verify that the exception message contains the expected err or message.
EXPECT_STREQ("Negative cycle detected in the graph.",ex.what());
throw;
}
},
std::invalid_argument);

}
template <typename T>
struct AStarShortestPathTest : public testing::Test {
using graph_t = typename T::first_type;
Expand Down

0 comments on commit 55cab98

Please sign in to comment.