From 583c900edb85de7e46d4dcfdfdcd588c8c203062 Mon Sep 17 00:00:00 2001 From: Arnaud Becheler <8360330+Becheler@users.noreply.github.com> Date: Wed, 7 Oct 2026 10:54:56 +0200 Subject: [PATCH] fix: bound sequential_vertex_coloring by vertices(g), not num_vertices(g) --- .../coloring/sequential_vertex_coloring.adoc | 6 +- .../graph/sequential_vertex_coloring.hpp | 10 +- test/sequential_vertex_coloring.cpp | 144 +++++++++++++++--- 3 files changed, 130 insertions(+), 30 deletions(-) diff --git a/doc/modules/ROOT/pages/algorithms/coloring/sequential_vertex_coloring.adoc b/doc/modules/ROOT/pages/algorithms/coloring/sequential_vertex_coloring.adoc index d6d185399..d326c2c3f 100644 --- a/doc/modules/ROOT/pages/algorithms/coloring/sequential_vertex_coloring.adoc +++ b/doc/modules/ROOT/pages/algorithms/coloring/sequential_vertex_coloring.adoc @@ -45,8 +45,10 @@ sequential_vertex_coloring( | IN | `OrderPA order` -| A mapping from integers in the range _[0, num_vertices(g))_ - to the vertices of the graph. +| A mapping from integers in the range _[0, n)_ to the vertices of the graph, + where _n_ is the number of vertices listed by `vertices(g)`. This is + `num_vertices(g)` for most graph types, but can be smaller for + xref:adaptors/filtered_graph.adoc[`filtered_graph`]. | OUT | `ColorMap color` diff --git a/include/boost/graph/sequential_vertex_coloring.hpp b/include/boost/graph/sequential_vertex_coloring.hpp index fcfa24301..028165157 100644 --- a/include/boost/graph/sequential_vertex_coloring.hpp +++ b/include/boost/graph/sequential_vertex_coloring.hpp @@ -2,6 +2,7 @@ // Copyright 1997, 1998, 1999, 2000 University of Notre Dame. // Copyright 2004 The Trustees of Indiana University // Authors: Andrew Lumsdaine, Lie-Quan Lee, Jeremy G. Siek +// Copyright (c) 2026 Arnaud Becheler // // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at @@ -15,10 +16,7 @@ #include #include #include - -#ifdef BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS #include -#endif /* This algorithm is to find coloring of a graph @@ -50,6 +48,10 @@ typename property_traits< ColorMap >::value_type sequential_vertex_coloring( size_type max_color = 0; const size_type V = num_vertices(G); + // The order only covers vertices(G), which filtered_graph leaves shorter than num_vertices(G). + const auto vertex_range = vertices(G); + const size_type n = static_cast< size_type >(std::distance(vertex_range.first, vertex_range.second)); + // We need to keep track of which colors are used by // adjacent vertices. We do this by marking the colors // that are used. The mark array contains the mark @@ -66,7 +68,7 @@ typename property_traits< ColorMap >::value_type sequential_vertex_coloring( put(color, *v, V - 1); // Determine the color for every vertex one by one - for (size_type i = 0; i < V; i++) + for (size_type i = 0; i < n; i++) { Vertex current = get(order, i); typename GraphTraits::adjacency_iterator v, vend; diff --git a/test/sequential_vertex_coloring.cpp b/test/sequential_vertex_coloring.cpp index 83500798b..f0cd9b0f3 100644 --- a/test/sequential_vertex_coloring.cpp +++ b/test/sequential_vertex_coloring.cpp @@ -1,4 +1,5 @@ // Copyright 2004 The Trustees of Indiana University. +// Copyright (c) 2026 Arnaud Becheler // Use, modification and distribution is subject to the Boost Software // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -9,17 +10,64 @@ #include #include #include +#include +#include +#include #include +#include -using namespace boost; +using SimpleGraph = ::boost::adjacency_list< ::boost::listS, ::boost::vecS, ::boost::undirectedS >; +using BaseGraph = ::boost::adjacency_list< ::boost::vecS, ::boost::vecS, ::boost::undirectedS >; +using BaseVertex = ::boost::graph_traits< BaseGraph >::vertex_descriptor; -int main(int, char*[]) +// Keeps the even numbered vertices, so vertices(g) is shorter than num_vertices(g). +struct keep_even { - typedef adjacency_list< listS, vecS, undirectedS > Graph; - typedef graph_traits< Graph >::vertices_size_type vertices_size_type; - typedef property_map< Graph, vertex_index_t >::const_type vertex_index_map; + bool operator()(BaseVertex v) const { return v % 2 == 0; } +}; - typedef std::pair< int, int > Edge; +using FilteredGraph = ::boost::filtered_graph< BaseGraph, ::boost::keep_all, keep_even >; +using size_type = ::boost::graph_traits< FilteredGraph >::vertices_size_type; + +// Reports out of range queries instead of letting the algorithm read past the order. +struct counting_order +{ + const std::vector< BaseVertex >* order; + std::size_t* out_of_range; + + BaseVertex operator()(size_type i) const + { + if (i < order->size()) + return (*order)[i]; + ++*out_of_range; + return order->front(); + } +}; + +BaseGraph make_base_graph() +{ + using Edge = std::pair< std::size_t, std::size_t >; + constexpr Edge edges[] = { Edge(0, 2), Edge(2, 4), Edge(1, 0), Edge(1, 2), Edge(3, 2), Edge(3, 4) }; + constexpr std::size_t m = sizeof(edges) / sizeof(Edge); + return BaseGraph(edges, edges + m, 5); +} + +std::vector< BaseVertex > vertex_order(const FilteredGraph& g) +{ + std::vector< BaseVertex > order; + const auto vertex_range = ::boost::vertices(g); + for (auto vi = vertex_range.first; vi != vertex_range.second; ++vi) + order.push_back(*vi); + return order; +} + +constexpr size_type sentinel = 99; + +void test_simple_graph() +{ + using vertex_descriptor = ::boost::graph_traits< SimpleGraph >::vertex_descriptor; + using vertices_size_type = ::boost::graph_traits< SimpleGraph >::vertices_size_type; + using Edge = std::pair< int, int >; enum nodes { A, @@ -29,22 +77,70 @@ int main(int, char*[]) E, n }; - Edge edge_array[] = { Edge(A, C), Edge(B, B), Edge(B, D), Edge(B, E), - Edge(C, B), Edge(C, D), Edge(D, E), Edge(E, A), Edge(E, B) }; - int m = sizeof(edge_array) / sizeof(Edge); - Graph g(edge_array, edge_array + m, n); - - // Test with the normal order - std::vector< vertices_size_type > color_vec(num_vertices(g)); - iterator_property_map< vertices_size_type*, vertex_index_map, - vertices_size_type, vertices_size_type& > - color(&color_vec.front(), get(vertex_index, g)); - vertices_size_type num_colors = sequential_vertex_coloring(g, color); - BOOST_TEST(num_colors == 3); - BOOST_TEST(get(color, (vertices_size_type)A) == 0); - BOOST_TEST(get(color, (vertices_size_type)B) == 0); - BOOST_TEST(get(color, (vertices_size_type)C) == 1); - BOOST_TEST(get(color, (vertices_size_type)D) == 2); - BOOST_TEST(get(color, (vertices_size_type)E) == 1); - return boost::report_errors(); + constexpr Edge edge_array[] = { Edge(A, C), Edge(B, B), Edge(B, D), Edge(B, E), Edge(C, B), Edge(C, D), Edge(D, E), Edge(E, A), Edge(E, B) }; + constexpr std::size_t m = sizeof(edge_array) / sizeof(Edge); + const SimpleGraph g(edge_array, edge_array + m, n); + + std::vector< vertices_size_type > color_vec(::boost::num_vertices(g)); + auto color_map = ::boost::make_iterator_property_map(color_vec.begin(), ::boost::get(::boost::vertex_index, g)); + const vertices_size_type num_colors = ::boost::sequential_vertex_coloring(g, color_map); + + BOOST_TEST_EQ(num_colors, 3); + BOOST_TEST_EQ(::boost::get(color_map, static_cast< vertex_descriptor >(A)), 0); + BOOST_TEST_EQ(::boost::get(color_map, static_cast< vertex_descriptor >(B)), 0); + BOOST_TEST_EQ(::boost::get(color_map, static_cast< vertex_descriptor >(C)), 1); + BOOST_TEST_EQ(::boost::get(color_map, static_cast< vertex_descriptor >(D)), 2); + BOOST_TEST_EQ(::boost::get(color_map, static_cast< vertex_descriptor >(E)), 1); +} + +// Regression for https://github.com/boostorg/graph/issues/627 +void test_filtered_graph_order_bounds() +{ + const BaseGraph base = make_base_graph(); + const FilteredGraph g(base, ::boost::keep_all(), keep_even()); + const std::vector< BaseVertex > order = vertex_order(g); + + BOOST_TEST_EQ(order.size(), 3); + BOOST_TEST_EQ(::boost::num_vertices(g), 5); + + std::size_t out_of_range = 0; + const counting_order query = { &order, &out_of_range }; + auto order_map = ::boost::make_function_property_map< size_type >(query); + + std::vector< size_type > color_vec(::boost::num_vertices(g), sentinel); + auto color_map = ::boost::make_iterator_property_map(color_vec.begin(), ::boost::get(::boost::vertex_index, g)); + const size_type num_colors = ::boost::sequential_vertex_coloring(g, order_map, color_map); + + BOOST_TEST_EQ(out_of_range, 0); + BOOST_TEST_EQ(num_colors, 2); + BOOST_TEST_EQ(color_vec[0], 0); + BOOST_TEST_EQ(color_vec[2], 1); + BOOST_TEST_EQ(color_vec[4], 0); + BOOST_TEST_EQ(color_vec[1], sentinel); + BOOST_TEST_EQ(color_vec[3], sentinel); +} + +void test_filtered_graph_default_order() +{ + const BaseGraph base = make_base_graph(); + const FilteredGraph g(base, ::boost::keep_all(), keep_even()); + + std::vector< size_type > color_vec(::boost::num_vertices(g), sentinel); + auto color_map = ::boost::make_iterator_property_map(color_vec.begin(), ::boost::get(::boost::vertex_index, g)); + const size_type num_colors = ::boost::sequential_vertex_coloring(g, color_map); + + BOOST_TEST_EQ(num_colors, 2); + BOOST_TEST_EQ(color_vec[0], 0); + BOOST_TEST_EQ(color_vec[2], 1); + BOOST_TEST_EQ(color_vec[4], 0); + BOOST_TEST_EQ(color_vec[1], sentinel); + BOOST_TEST_EQ(color_vec[3], sentinel); +} + +int main() +{ + test_simple_graph(); + test_filtered_graph_order_bounds(); + test_filtered_graph_default_order(); + return ::boost::report_errors(); }