Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,8 @@ image::figs/biconnected.png[]

Vertices can be present in multiple biconnected components, but each edge can only be contained in a single biconnected component. The output of the `biconnected_components` algorithm records the biconnected component number of each edge in the property map `comp`. Articulation points will be emitted to the (optional) output iterator argument to `biconnected_components`, or can be computed without the use of a biconnected component number map via `articulation_points`. These functions return the number of biconnected components, the articulation point output iterator, or a pair of these quantities, depending on what information was recorded.

Parallel edges are supported: each parallel edge is assigned the component number of its block. Self-loops are not supported: the component number recorded for a self-loop is unspecified and may not be written at all.

The algorithm implemented is due to Tarjan [xref:about/bibliography.adoc#tarjan72:dfs_and_linear_algo[36]].

== Returns
Expand Down
4 changes: 3 additions & 1 deletion include/boost/graph/biconnected_components.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Copyright (c) Jeremy Siek 2001
// Copyright (c) Douglas Gregor 2004
// Copyright (c) 2026 Arnaud Becheler
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
Expand Down Expand Up @@ -109,9 +110,10 @@ namespace detail
= source(e, g);
typename boost::graph_traits< Graph >::vertex_descriptor tgt
= target(e, g);
// Parallel edges to the parent also reach here and need a label.
S.push(e);
if (tgt != get(pred, src))
{
S.push(e);
put(lowpt, src,
min BOOST_PREVENT_MACRO_SUBSTITUTION(
get(lowpt, src), get(dtm, tgt)));
Expand Down
50 changes: 50 additions & 0 deletions test/biconnected_components_test.cpp
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -17,10 +18,13 @@
#include <boost/graph/connected_components.hpp>
#include <boost/graph/random.hpp>
#include <boost/random/linear_congruential.hpp>
#include <boost/range/iterator_range.hpp>
#include <fstream>

#include <cstddef>
#include <functional>
#include <limits>
#include <utility>

using namespace boost;

Expand Down Expand Up @@ -168,6 +172,51 @@ void test_stateful_visitor_with_ref()
BOOST_TEST_EQ(copied.count, static_cast< std::size_t >(0));
}

// endpoints of an undirected edge, smallest first
std::pair< Vertex, Vertex > sorted_endpoints(boost::graph_traits< Graph >::edge_descriptor e, const Graph& g)
{
const auto u = boost::source(e, g);
const auto v = boost::target(e, g);
return std::make_pair(std::min(u, v), std::max(u, v));
}

// every parallel edge gets the label of its block
void test_parallel_edges()
{
Graph g(6);
boost::add_edge(0, 1, g);
boost::add_edge(0, 1, g);
boost::add_edge(0, 1, g);
boost::add_edge(1, 2, g);
boost::add_edge(2, 3, g);
boost::add_edge(2, 3, g);
boost::add_edge(2, 3, g);
boost::add_edge(3, 4, g);
boost::add_edge(4, 5, g);

constexpr std::size_t unlabeled = std::numeric_limits< std::size_t >::max();
auto component_map = boost::get(&EdgeProperty::component, g);
for (const auto e : boost::make_iterator_range(boost::edges(g)))
{
boost::put(component_map, e, unlabeled);
}

const auto num_comps = boost::biconnected_components(g, component_map);
BOOST_TEST_EQ(num_comps, static_cast< std::size_t >(5));

// each block of this graph is exactly one bundle of parallel edges
for (const auto e : boost::make_iterator_range(boost::edges(g)))
{
BOOST_TEST_NE(component_map[e], unlabeled);
for (const auto f : boost::make_iterator_range(boost::edges(g)))
{
const bool parallel = sorted_endpoints(e, g) == sorted_endpoints(f, g);
const bool same_component = component_map[e] == component_map[f];
BOOST_TEST_EQ(parallel, same_component);
}
}
}

int main(int argc, char* argv[])
{
std::size_t n = 100;
Expand Down Expand Up @@ -198,6 +247,7 @@ int main(int argc, char* argv[])
}

test_stateful_visitor_with_ref();
test_parallel_edges();

return boost::report_errors();
}
Loading