Skip to content

Commit

Permalink
enhancement: Catch negative edge weights in dijkstra (#67)
Browse files Browse the repository at this point in the history
* Throw error on negative edge weights
* Add test for negative edge weights
  • Loading branch information
joweich authored Aug 13, 2023
1 parent 0844d8b commit bbd7e90
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 2 deletions.
11 changes: 9 additions & 2 deletions include/graaflib/algorithm/shortest_path.tpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,15 @@ std::optional<graph_path<WEIGHT_T>> dijkstra_shortest_path(
}

for (const auto& neighbor : graph.get_neighbors(current.id)) {
WEIGHT_T distance = current.dist_from_start +
get_weight(graph.get_edge(current.id, neighbor));
WEIGHT_T edge_weight = get_weight(graph.get_edge(current.id, neighbor));

if (edge_weight < 0) {
throw std::invalid_argument{fmt::format(
"Negative edge weight [{}] between vertices [{}] -> [{}].",
edge_weight, current.id, neighbor)};
}

WEIGHT_T distance = current.dist_from_start + edge_weight;

if (!vertex_info.contains(neighbor) ||
distance < vertex_info[neighbor].dist_from_start) {
Expand Down
83 changes: 83 additions & 0 deletions test/graaflib/algorithm/shortest_path_test.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include <fmt/core.h>
#include <graaflib/algorithm/shortest_path.h>
#include <graaflib/graph.h>
#include <graaflib/types.h>
Expand Down Expand Up @@ -410,4 +411,86 @@ TYPED_TEST(DijkstraShortestPathTest, DijkstraMoreComplexShortestPathTree) {
ASSERT_EQ(path_map, expected_path_map);
}

template <typename T>
struct DijkstraShortestPathSignedTypesTest : public testing::Test {
using graph_t = typename T::first_type;
using edge_t = typename T::second_type;
};

using 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>>,

/**
* Primitive edge type undirected graph
*/
std::pair<undirected_graph<int, int>, int>,
std::pair<undirected_graph<int, float>, float>,
std::pair<undirected_graph<int, long double>, long double>,

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

TYPED_TEST_SUITE(DijkstraShortestPathSignedTypesTest,
weighted_graph_signed_types);

TYPED_TEST(DijkstraShortestPathSignedTypesTest, DijkstraNegativeWeight) {
// 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{};

const auto vertex_id_1{graph.add_vertex(10)};
const auto vertex_id_2{graph.add_vertex(20)};
graph.add_edge(vertex_id_1, vertex_id_2, edge_t{static_cast<weight_t>(-1)});

// THEN
ASSERT_THROW(
{
try {
// Call the get_edge function for non-existing vertices
[[maybe_unused]] const auto path{
dijkstra_shortest_path(graph, vertex_id_1, vertex_id_2)};
// 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 error
// message
EXPECT_EQ(
ex.what(),
fmt::format(
"Negative edge weight [{}] between vertices [{}] -> [{}].",
-1, vertex_id_1, vertex_id_2));
throw;
}
},
std::invalid_argument);
}
} // namespace graaf::algorithm

0 comments on commit bbd7e90

Please sign in to comment.