From d1189913d8a33e0875d63c3e928264db84ce799e Mon Sep 17 00:00:00 2001 From: dcoudert Date: Wed, 1 Oct 2025 16:39:17 +0200 Subject: [PATCH 1/3] add method number_of_biconnected_components --- src/sage/graphs/connectivity.pyx | 65 ++++++++++++++++++++++++++++++++ src/sage/graphs/generic_graph.py | 2 + 2 files changed, 67 insertions(+) diff --git a/src/sage/graphs/connectivity.pyx b/src/sage/graphs/connectivity.pyx index b1f6db2b54f..fe8117d50e6 100644 --- a/src/sage/graphs/connectivity.pyx +++ b/src/sage/graphs/connectivity.pyx @@ -23,6 +23,7 @@ Here is what the module can do: :meth:`blocks_and_cut_vertices` | Return the blocks and cut vertices of the graph. :meth:`blocks_and_cuts_tree` | Return the blocks-and-cuts tree of the graph. :meth:`biconnected_components_subgraphs` | Return a list of biconnected components as graph objects. + :meth:`number_of_biconnected_components` | Return the number of biconnected components. :meth:`is_cut_edge` | Check whether the input edge is a cut-edge or a bridge. :meth:`is_edge_cut` | Check whether the input edges form an edge cut. :meth:`is_cut_vertex` | Check whether the input vertex is a cut-vertex. @@ -818,6 +819,70 @@ def biconnected_components_subgraphs(G): return [G.subgraph(c) for c in blocks_and_cut_vertices(G)[0]] +def number_of_biconnected_components(G): + r""" + Return the number of biconnected components. + + A biconnected component is a maximal subgraph on two or more vertices that + is biconnected, i.e., removing any vertex does not disconnect it. + + .. SEEALSO:: + + - :meth:`~sage.graphs.generic_graph.GenericGraph.blocks_and_cut_vertices` + - :meth:`~Graph.is_biconnected` + + EXAMPLES: + + The disjoint union of cycles has as many biconnected components as the + number of cycles:: + + sage: G = graphs.CycleGraph(5) + sage: G.number_of_biconnected_components() + 1 + sage: (3 * G).number_of_biconnected_components() + 3 + + A block graph is a connected graph in which every biconnected component + (block) is a clique. Hence its number of biconnected components is its + number of blocks:: + + sage: number_of_blocks = randint(4, 10) + sage: G = graphs.RandomBlockGraph(number_of_blocks, 5) + sage: G.number_of_biconnected_components() == number_of_blocks + True + + By definition, an edge is a biconnected component. Hence, the number of + biconnected components of a tree is its number of edges:: + + sage: T = graphs.RandomTree(randint(0, 10)) + sage: T.number_of_biconnected_components() == T.size() + True + + An isolated vertex is a block but not a biconnected component:: + + sage: G = Graph(3) + sage: len(G.blocks_and_cut_vertices()[0]) + 3 + sage: G.number_of_biconnected_components() + 0 + + TESTS: + + An error is raised if the input is not a Sage graph:: + + sage: from sage.graphs.connectivity import number_of_biconnected_components + sage: number_of_biconnected_components('I am not a graph') + Traceback (most recent call last): + ... + TypeError: the input must be a Sage graph + """ + from sage.graphs.generic_graph import GenericGraph + if not isinstance(G, GenericGraph): + raise TypeError("the input must be a Sage graph") + + return len([c for c in G.blocks_and_cut_vertices()[0] if len(c) > 1]) + + def is_edge_cut(G, edges): """ Check whether ``edges`` form an edge cut. diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 847ce71bf05..62cf9b873ae 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -245,6 +245,7 @@ :meth:`~GenericGraph.blocks_and_cut_vertices` | Compute the blocks and cut vertices of the graph. :meth:`~GenericGraph.blocks_and_cuts_tree` | Compute the blocks-and-cuts tree of the graph. :meth:`~GenericGraph.biconnected_components_subgraphs` | Return a list of biconnected components as graph objects. + :meth:`~GenericGraph.number_of_biconnected_components` | Return the number of biconnected components. :meth:`~GenericGraph.is_cut_edge` | Check whether the input edge is a cut-edge or a bridge. :meth:`~GenericGraph.is_edge_cut` | Check whether the input edges form an edge cut. :meth:`~GenericGraph.is_cut_vertex` | Check whether the input vertex is a cut-vertex. @@ -26005,6 +26006,7 @@ def is_self_complementary(self): is_cut_vertex, is_edge_cut, is_vertex_cut, + number_of_biconnected_components, vertex_connectivity, ) from sage.graphs.distances_all_pairs import distances_distribution, szeged_index From 7d5b2277fe53a87b26a30a8cb4e7d9037ec1e275 Mon Sep 17 00:00:00 2001 From: dcoudert Date: Wed, 1 Oct 2025 17:38:47 +0200 Subject: [PATCH 2/3] add method biconnected_components --- src/sage/graphs/connectivity.pyx | 46 +++++++++++++++++++++++++++++--- src/sage/graphs/generic_graph.py | 2 ++ 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/src/sage/graphs/connectivity.pyx b/src/sage/graphs/connectivity.pyx index fe8117d50e6..e14bf7d45cb 100644 --- a/src/sage/graphs/connectivity.pyx +++ b/src/sage/graphs/connectivity.pyx @@ -22,6 +22,7 @@ Here is what the module can do: :meth:`connected_components_sizes` | Return the sizes of the connected components as a list. :meth:`blocks_and_cut_vertices` | Return the blocks and cut vertices of the graph. :meth:`blocks_and_cuts_tree` | Return the blocks-and-cuts tree of the graph. + :meth:`biconnected_components` | Return the list of biconnected components. :meth:`biconnected_components_subgraphs` | Return a list of biconnected components as graph objects. :meth:`number_of_biconnected_components` | Return the number of biconnected components. :meth:`is_cut_edge` | Check whether the input edge is a cut-edge or a bridge. @@ -779,12 +780,49 @@ def blocks_and_cuts_tree(G): return g +def biconnected_components(G): + r""" + Return the list of biconnected components. + + A biconnected component is a maximal subgraph on two or more vertices that + is biconnected, i.e., removing any vertex does not disconnect it. + + INPUT: + + - ``G`` -- the input graph + + EXAMPLES:: + + sage: from sage.graphs.connectivity import biconnected_components + sage: G = Graph({0: [1, 2], 1: [0, 2], 2: [0, 1, 3], 3: [2]}) + sage: sorted(len(b) for b in biconnected_components(G)) + [2, 3] + sage: sorted(len(b) for b in biconnected_components(2 * G)) + [2, 2, 3, 3] + + TESTS: + + If ``G`` is not a Sage graph, an error is raised:: + + sage: from sage.graphs.connectivity import biconnected_components + sage: biconnected_components('I am not a graph') + Traceback (most recent call last): + ... + TypeError: the input must be a Sage graph + """ + from sage.graphs.generic_graph import GenericGraph + if not isinstance(G, GenericGraph): + raise TypeError("the input must be a Sage graph") + + return [b for b in blocks_and_cut_vertices(G)[0] if len(b) > 1] + + def biconnected_components_subgraphs(G): r""" Return a list of biconnected components as graph objects. - A biconnected component is a maximal subgraph that is biconnected, i.e., - removing any vertex does not disconnect it. + A biconnected component is a maximal subgraph on two or more vertices that + is biconnected, i.e., removing any vertex does not disconnect it. INPUT: @@ -816,7 +854,7 @@ def biconnected_components_subgraphs(G): if not isinstance(G, GenericGraph): raise TypeError("the input must be a Sage graph") - return [G.subgraph(c) for c in blocks_and_cut_vertices(G)[0]] + return [G.subgraph(c) for c in G.biconnected_components()] def number_of_biconnected_components(G): @@ -880,7 +918,7 @@ def number_of_biconnected_components(G): if not isinstance(G, GenericGraph): raise TypeError("the input must be a Sage graph") - return len([c for c in G.blocks_and_cut_vertices()[0] if len(c) > 1]) + return len(G.biconnected_components()) def is_edge_cut(G, edges): diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 62cf9b873ae..eda36cda5cf 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -244,6 +244,7 @@ :meth:`~GenericGraph.connected_components_sizes` | Return the sizes of the connected components as a list. :meth:`~GenericGraph.blocks_and_cut_vertices` | Compute the blocks and cut vertices of the graph. :meth:`~GenericGraph.blocks_and_cuts_tree` | Compute the blocks-and-cuts tree of the graph. + :meth:`~GenericGraph.biconnected_components` | Return the list of biconnected components. :meth:`~GenericGraph.biconnected_components_subgraphs` | Return a list of biconnected components as graph objects. :meth:`~GenericGraph.number_of_biconnected_components` | Return the number of biconnected components. :meth:`~GenericGraph.is_cut_edge` | Check whether the input edge is a cut-edge or a bridge. @@ -25992,6 +25993,7 @@ def is_self_complementary(self): from sage.graphs.base.static_dense_graph import connected_subgraph_iterator from sage.graphs.base.static_sparse_graph import spectral_radius from sage.graphs.connectivity import ( + biconnected_components, biconnected_components_subgraphs, blocks_and_cut_vertices, blocks_and_cuts_tree, From 196151d9ce718d1fe4cdd08cd20625fd706c3293 Mon Sep 17 00:00:00 2001 From: dcoudert Date: Wed, 1 Oct 2025 17:49:13 +0200 Subject: [PATCH 3/3] move method is_biconnected from graph.py to connectivity.pyx --- src/sage/graphs/connectivity.pyx | 53 ++++++++++++++++++++--- src/sage/graphs/generic_graph.py | 4 +- src/sage/graphs/graph.py | 40 ----------------- src/sage/graphs/isgci.py | 2 +- src/sage/graphs/matching_covered_graph.py | 4 +- 5 files changed, 53 insertions(+), 50 deletions(-) diff --git a/src/sage/graphs/connectivity.pyx b/src/sage/graphs/connectivity.pyx index e14bf7d45cb..15b238050ae 100644 --- a/src/sage/graphs/connectivity.pyx +++ b/src/sage/graphs/connectivity.pyx @@ -22,6 +22,7 @@ Here is what the module can do: :meth:`connected_components_sizes` | Return the sizes of the connected components as a list. :meth:`blocks_and_cut_vertices` | Return the blocks and cut vertices of the graph. :meth:`blocks_and_cuts_tree` | Return the blocks-and-cuts tree of the graph. + :meth:`is_biconnected` | Check whether the graph is biconnected. :meth:`biconnected_components` | Return the list of biconnected components. :meth:`biconnected_components_subgraphs` | Return a list of biconnected components as graph objects. :meth:`number_of_biconnected_components` | Return the number of biconnected components. @@ -92,7 +93,7 @@ def is_connected(G, forbidden_vertices=None): .. SEEALSO:: - - :meth:`~Graph.is_biconnected` + - :meth:`~sage.graphs.generic_graph.GenericGraph.is_biconnected` EXAMPLES:: @@ -507,7 +508,7 @@ def blocks_and_cut_vertices(G, algorithm='Tarjan_Boost', sort=False, key=None): - :meth:`blocks_and_cuts_tree` - :func:`sage.graphs.base.boost_graph.blocks_and_cut_vertices` - - :meth:`~Graph.is_biconnected` + - :meth:`~sage.graphs.generic_graph.GenericGraph.is_biconnected` - :meth:`~Graph.bridges` EXAMPLES: @@ -718,7 +719,7 @@ def blocks_and_cuts_tree(G): .. SEEALSO:: - :meth:`~sage.graphs.generic_graph.GenericGraph.blocks_and_cut_vertices` - - :meth:`~Graph.is_biconnected` + - :meth:`~sage.graphs.generic_graph.GenericGraph.is_biconnected` EXAMPLES:: @@ -780,6 +781,46 @@ def blocks_and_cuts_tree(G): return g +def is_biconnected(G): + r""" + Check whether the graph is biconnected. + + A biconnected graph is a connected graph on two or more vertices that is not + broken into disconnected pieces by deleting any single vertex. + + .. SEEALSO:: + + - :meth:`~sage.graphs.generic_graph.GenericGraph.is_connected` + - :meth:`~sage.graphs.generic_graph.GenericGraph.blocks_and_cut_vertices` + - :meth:`~sage.graphs.generic_graph.GenericGraph.blocks_and_cuts_tree` + - :wikipedia:`Biconnected_graph` + + EXAMPLES:: + + sage: G = graphs.PetersenGraph() + sage: G.is_biconnected() + True + sage: G.add_path([0,'a','b']) + sage: G.is_biconnected() + False + sage: G.add_edge('b', 1) + sage: G.is_biconnected() + True + + TESTS:: + + sage: Graph().is_biconnected() + False + sage: Graph(1).is_biconnected() + False + sage: graphs.CompleteGraph(2).is_biconnected() + True + """ + if G.order() < 2 or not G.is_connected(): + return False + return not G.blocks_and_cut_vertices()[1] + + def biconnected_components(G): r""" Return the list of biconnected components. @@ -867,7 +908,7 @@ def number_of_biconnected_components(G): .. SEEALSO:: - :meth:`~sage.graphs.generic_graph.GenericGraph.blocks_and_cut_vertices` - - :meth:`~Graph.is_biconnected` + - :meth:`~sage.graphs.generic_graph.GenericGraph.is_biconnected` EXAMPLES: @@ -3466,7 +3507,7 @@ cdef class TriconnectivitySPQR: .. SEEALSO:: - :meth:`sage.graphs.connectivity.spqr_tree` - - :meth:`~Graph.is_biconnected` + - :meth:`~sage.graphs.generic_graph.GenericGraph.is_biconnected` - :wikipedia:`SPQR_tree` EXAMPLES: @@ -4892,7 +4933,7 @@ def is_triconnected(G): .. SEEALSO:: - :meth:`~sage.graphs.generic_graph.GenericGraph.is_connected` - - :meth:`~Graph.is_biconnected` + - :meth:`~sage.graphs.generic_graph.GenericGraph.is_biconnected` - :meth:`~sage.graphs.connectivity.spqr_tree` - :wikipedia:`SPQR_tree` diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index eda36cda5cf..9292b2d30ae 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -236,7 +236,7 @@ :widths: 30, 70 :delim: | - :meth:`~GenericGraph.is_connected` | Test whether the (di)graph is connected. + :meth:`~GenericGraph.is_connected` | Check whether the (di)graph is connected. :meth:`~GenericGraph.connected_components` | Return the list of connected components :meth:`~GenericGraph.connected_components_number` | Return the number of connected components. :meth:`~GenericGraph.connected_components_subgraphs` | Return a list of connected components as graph objects. @@ -244,6 +244,7 @@ :meth:`~GenericGraph.connected_components_sizes` | Return the sizes of the connected components as a list. :meth:`~GenericGraph.blocks_and_cut_vertices` | Compute the blocks and cut vertices of the graph. :meth:`~GenericGraph.blocks_and_cuts_tree` | Compute the blocks-and-cuts tree of the graph. + :meth:`~GenericGraph.is_biconnected` | Check whether the graph is biconnected. :meth:`~GenericGraph.biconnected_components` | Return the list of biconnected components. :meth:`~GenericGraph.biconnected_components_subgraphs` | Return a list of biconnected components as graph objects. :meth:`~GenericGraph.number_of_biconnected_components` | Return the number of biconnected components. @@ -26003,6 +26004,7 @@ def is_self_complementary(self): connected_components_sizes, connected_components_subgraphs, edge_connectivity, + is_biconnected, is_connected, is_cut_edge, is_cut_vertex, diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index 248fd65d12f..c5e36ce9c6d 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -1754,46 +1754,6 @@ def is_cactus(self): B = self.blocks_and_cut_vertices()[0] return len(self.faces()) == sum(1 for b in B if len(b) > 2) + 1 - @doc_index("Graph properties") - def is_biconnected(self): - """ - Test if the graph is biconnected. - - A biconnected graph is a connected graph on two or more vertices that is - not broken into disconnected pieces by deleting any single vertex. - - .. SEEALSO:: - - - :meth:`~sage.graphs.generic_graph.GenericGraph.is_connected` - - :meth:`~sage.graphs.generic_graph.GenericGraph.blocks_and_cut_vertices` - - :meth:`~sage.graphs.generic_graph.GenericGraph.blocks_and_cuts_tree` - - :wikipedia:`Biconnected_graph` - - EXAMPLES:: - - sage: G = graphs.PetersenGraph() - sage: G.is_biconnected() - True - sage: G.add_path([0,'a','b']) - sage: G.is_biconnected() - False - sage: G.add_edge('b', 1) - sage: G.is_biconnected() - True - - TESTS:: - - sage: Graph().is_biconnected() - False - sage: Graph(1).is_biconnected() - False - sage: graphs.CompleteGraph(2).is_biconnected() - True - """ - if self.order() < 2 or not self.is_connected(): - return False - return not self.blocks_and_cut_vertices()[1] - @doc_index("Graph properties") def is_block_graph(self): r""" diff --git a/src/sage/graphs/isgci.py b/src/sage/graphs/isgci.py index 5efedd77616..d00bb9597e7 100644 --- a/src/sage/graphs/isgci.py +++ b/src/sage/graphs/isgci.py @@ -144,7 +144,7 @@ * - Biconnected - - :meth:`~sage.graphs.graph.Graph.is_biconnected`, + - :meth:`~sage.graphs.generic_graph.GenericGraph.is_biconnected`, :meth:`~sage.graphs.generic_graph.GenericGraph.blocks_and_cut_vertices`, :meth:`~sage.graphs.generic_graph.GenericGraph.blocks_and_cuts_tree` diff --git a/src/sage/graphs/matching_covered_graph.py b/src/sage/graphs/matching_covered_graph.py index 5d7e2ba49b8..48756e1e389 100644 --- a/src/sage/graphs/matching_covered_graph.py +++ b/src/sage/graphs/matching_covered_graph.py @@ -2387,8 +2387,8 @@ def is_biconnected(self): .. NOTE:: This method overwrites the - :meth:`~sage.graphs.graph.Graph.is_biconnected` method - in order to return ``True`` as matching covered graphs are + :meth:`~sage.graphs.generic_graph.GenericGraph.is_biconnected` + method in order to return ``True`` as matching covered graphs are biconnected. EXAMPLES: