Repository navigation
Description
num_vertices(g) is used for two different things in BGL:
- as a vertex count (emptiness checks, loop bounds, sampling)
graph/include/boost/graph/connected_components.hpp
Lines 72 to 73 in ddce215
if (num_vertices(g) == 0) return 0; - and as an upper bound on vertex indices to size property arrays.
graph/include/boost/graph/connected_components.hpp
Lines 106 to 108 in ddce215
auto index_map = get(vertex_index, g); auto color_map = ::boost::make_two_bit_color_map(num_vertices(g), index_map); depth_first_search(g, vis, color_map);
Note
This works today because filtering doesn't renumber vertices: a kept vertex keeps its index from the base graph, so indices still go up to the base graph's size. filtered_graph forwards num_vertices and num_edges to the base graph, so any array sized with num_vertices(fg) is large enough for every index still in use. The price is that num_vertices no longer means "how many vertices vertices(fg) yields".
// base graph has vertices 0 1 2 3 4, the filter keeps 0 2 4
std::vector<color> color_map(num_vertices(fg));
// num_vertices(fg) is 5, so color_map[4] is valid
// if it returned 3, color_map[4] would be out of bounds
color_map[get(vertex_index, fg, 4)] = ...;Bug: Since num_vertices(fg) doesn't match what vertices(fg) yields, every algorithm that uses it as a count can go wrong once vertices are filtered out:
connected_componentsreturnsmax() + 1on an empty filtered graph: #231sequential_vertex_coloringread out of bounds: #627, fixed in #630random_vertex/random_edgecan advance past the end of the filtered rangevf2_sub_graph_isonever reports a mapping on a vertex-filtered graph. The docs say it was patched to usestd::distance, but the fix in #282 was never merged.scale_graphandwarren_transitive_closuredereference or step past the end when every vertex is filtered outstoer_wagner_min_cuttakes its phase count fromnum_vertices
Users hit this too: Rosetta, Hyperscan and IfcOpenShell all carry comments and workarounds for it, and BGL has its own count_vertices in isomorphism.hpp.
Proposal
- Add
vertex_index_bound(g)/edge_index_bound(g)(names TBD), defaulting tonum_vertices(g)/num_edges(g). Nothing changes for existing graphs. - Move algorithms to the bound when sizing property storage, and keep
num_verticesfor counting. - Then make
filtered_graph'snum_vertices/num_edgesreturn the real count (O(V) or cached, O(1) when the predicate iskeep_all).
Step 3 silently breaks user code that does std::vector<T> v(num_vertices(fg)) on a vertex-filtered graph and indexes it by vertex index. It compiles and then writes out of bounds. A search of public code found only a handful of such cases (bark, two OpenCV books, a BGL test), but it should still go with a release that already breaks things, plus a loud release note.
For #231 itself, fixing the call sites is enough.
Activity