From a8f8df55545c6e0c2e66e6b3093445dde491c0f2 Mon Sep 17 00:00:00 2001 From: "grishuhin.g" Date: Wed, 13 Aug 2025 21:01:42 +0300 Subject: [PATCH 1/2] Added unit-test for issue 434 (biconnected components for multiedges) --- test/biconnected_components_test.cpp | 131 +++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) diff --git a/test/biconnected_components_test.cpp b/test/biconnected_components_test.cpp index 0990b4ed6..302ac2f23 100644 --- a/test/biconnected_components_test.cpp +++ b/test/biconnected_components_test.cpp @@ -119,6 +119,109 @@ bool test_graph(Graph& g) return any_errors; } + +template < typename Graph, typename Vertex > +void check_components_index_for_multi_edges(const Graph& g, const std::vector< Vertex >& art_points) +{ + std::vector< int > components(num_vertices(g)); + int basic_comps = connected_components(g, + make_iterator_property_map( + components.begin(), get(vertex_index, g), int())); + + std::map, std::size_t> edge_component_map; + + bool multiple_edges_are_in_same_component = true; + + typename graph_traits::edge_iterator ei, ei_end; + for (boost::tie(ei, ei_end) = edges(g); ei != ei_end; ++ei) { + // Create canonical representation of edge (ordered pair) + std::pair edge(source(*ei, g), target(*ei, g)); + std::size_t component = g[*ei].component; + + // This is a multi-edge if we've seen the same vertex pair before + // All parallel edges between the same vertices must have the same component index + if (edge_component_map.count(edge)) { + std::size_t expected_component = edge_component_map.at(edge); + if (expected_component != component) + multiple_edges_are_in_same_component = false; + } + else { + edge_component_map[edge] = component; + } + + } + + BOOST_TEST(multiple_edges_are_in_same_component == true); + + if (!multiple_edges_are_in_same_component) + { + std::cerr << "ERROR!" << std::endl; + std::cerr << "\tComputed: \n"; + std::size_t multiedge_component_index; + for (boost::tie(ei, ei_end) = edges(g); ei != ei_end; ++ei) + std::cout << (char)(source(*ei, g) + 'A') << " -- " + << (char)(target(*ei, g) + 'A') + << " " << g[*ei].component << "\n"; + + std::cout << std::endl << "\tExpected: \n"; + for (boost::tie(ei, ei_end) = edges(g); ei != ei_end; ++ei) + { + std::pair edge(source(*ei, g), target(*ei, g)); + + // Print source and target vertices and expected component index + std::cout << (char)(source(*ei, g) + 'A') << " -- " + << (char)(target(*ei, g) + 'A') + << " " << (char)('0' + edge_component_map[edge]) + << "\n"; + } + std::cout << std::endl; + any_errors = true; + } + else + std::cout << "OK." << std::endl; +} + +/// @brief Verify that all edges in biconnected components consisting only of multi-edges have valid EdgeProperty values +bool test_graph_with_multi_edges(Graph& g) +{ // Returns false on failure + std::vector< Vertex > art_points; + + std::cout << "Computing biconnected components & articulation points... "; + std::cout.flush(); + + std::size_t num_comps = biconnected_components( + g, get(&EdgeProperty::component, g), std::back_inserter(art_points)) + .first; + + std::cout << "done.\n\t" << num_comps << " biconnected components.\n" + << "\t" << art_points.size() << " articulation points.\n" + << "\tTesting edge properties of multi-edges ..."; + std::cout.flush(); + + check_components_index_for_multi_edges(g, art_points); + + if (any_errors) + { + std::ofstream out("biconnected_components_on_multiedges_test_failed.dot"); + + out << "graph A {\n" + << " node[shape=\"circle\"]\n"; + + for (std::size_t i = 0; i < art_points.size(); ++i) + { + out << art_points[i] << " [ style=\"filled\" ];" << std::endl; + } + + graph_traits< Graph >::edge_iterator ei, ei_end; + for (boost::tie(ei, ei_end) = edges(g); ei != ei_end; ++ei) + out << source(*ei, g) << " -- " << target(*ei, g) << "[label=\"" + << g[*ei].component << "\"]\n"; + out << "}\n"; + } + + return any_errors; +} + int main(int argc, char* argv[]) { std::size_t n = 100; @@ -150,5 +253,33 @@ int main(int argc, char* argv[]) return 1; } + // Test case: Multiple edges forming biconnected components + // This test verifies that parallel edges (multi-edges) within biconnected components + // are correctly assigned the same component index + { + Graph g(5); + + // block of parallel edges + add_edge(0, 1, g); + //add_edge(0, 1, g); + //add_edge(0, 1, g); + + // bridge + add_edge(1, 2, g); + + // block of parallel edges + add_edge(2, 3, g); + //add_edge(2, 3, g); + //add_edge(2, 3, g); + + // another bridges + add_edge(3, 4, g); + add_edge(4, 5, g); + + if (test_graph_with_multi_edges(g)) + return 1; + + } + return boost::report_errors(); } From 6ae64e0aa26de0781d4be1e8b4f67fefa087d8c9 Mon Sep 17 00:00:00 2001 From: "grishuhin.g" Date: Wed, 13 Aug 2025 21:10:57 +0300 Subject: [PATCH 2/2] fix --- test/biconnected_components_test.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/biconnected_components_test.cpp b/test/biconnected_components_test.cpp index 302ac2f23..94e382cb3 100644 --- a/test/biconnected_components_test.cpp +++ b/test/biconnected_components_test.cpp @@ -261,16 +261,16 @@ int main(int argc, char* argv[]) // block of parallel edges add_edge(0, 1, g); - //add_edge(0, 1, g); - //add_edge(0, 1, g); + add_edge(0, 1, g); + add_edge(0, 1, g); // bridge add_edge(1, 2, g); // block of parallel edges add_edge(2, 3, g); - //add_edge(2, 3, g); - //add_edge(2, 3, g); + add_edge(2, 3, g); + add_edge(2, 3, g); // another bridges add_edge(3, 4, g);