Connectivity related functions - Graph Theory

Skip to content
Back to top
View this page
Edit this page

# Connectivity related functions

This module implements the connectivity based functions for graphs and digraphs. The methods in this module are also available as part of GenericGraph, DiGraph or Graph classes as aliases, and these methods can be accessed through this module or as class methods. Here is what the module can do:

For both directed and undirected graphs:

`is_connected()` | Check whether the (di)graph is connected.
`connected_components()` | Return the list of connected components
`number_of_connected_components()` | Return the number of connected components.
`connected_components_subgraphs()` | Return a list of connected components as graph objects.
`connected_component_containing_vertex()` | Return a list of the vertices connected to vertex.
`connected_components_sizes()` | Return the sizes of the connected components as a list.
`blocks_and_cut_vertices()` | Return the blocks and cut vertices of the graph.
`blocks_and_cuts_tree()` | Return the blocks-and-cuts tree of the graph.
`is_biconnected()` | Check whether the graph is biconnected.
`biconnected_components()` | Return the list of biconnected components.
`biconnected_components_subgraphs()` | Return a list of biconnected components as graph objects.
`number_of_biconnected_components()` | Return the number of biconnected components.
`is_cut_edge()` | Check whether the input edge is a cut-edge or a bridge.
`is_edge_cut()` | Check whether the input edges form an edge cut.
`is_cut_vertex()` | Check whether the input vertex is a cut-vertex.
`is_vertex_cut()` | Check whether the input vertices form a vertex cut.
`edge_connectivity()` | Return the edge connectivity of the graph.
`vertex_connectivity()` | Return the vertex connectivity of the graph.

For DiGraph:

`is_strongly_connected()` | Check whether the current `DiGraph` is strongly connected.
`strongly_connected_components_digraph()` | Return the digraph of the strongly connected components
`strongly_connected_components_subgraphs()` | Return the strongly connected components as a list of subgraphs.
`strongly_connected_component_containing_vertex()` | Return the strongly connected component containing a given vertex.
`strong_articulation_points()` | Return the strong articulation points of this digraph.

For undirected graphs:

`bridges()` | Return an iterator over the bridges (or cut edges) of given undirected graph.
`cleave()` | Return the connected subgraphs separated by the input vertex cut.
`is_triconnected()` | Check whether the graph is triconnected.
`spqr_tree()` | Return a SPQR-tree representing the triconnected components of the graph.
`spqr_tree_to_graph()` | Return the graph represented by the SPQR-tree \(T\).
`minimal_separators()` | Return an iterator over the minimal separators of `G`.

## Methods

class sage.graphs.connectivity.TriconnectivitySPQR

  Bases: `object`

  Decompose a graph into triconnected components and build SPQR-tree.

  This class implements the algorithm proposed by Hopcroft and Tarjan in [Hopcroft1973], and later corrected by Gutwenger and Mutzel in [Gut2001], for finding the triconnected components of a biconnected graph. It then organizes these components into a SPQR-tree. See the:wikipedia:\(SPQR_tree\).

  A SPQR-tree is a tree data structure used to represent the triconnected components of a biconnected (multi)graph and the 2-vertex cuts separating them. A node of a SPQR-tree, and the graph associated with it, can be one of the following four types:

  - `'S'` – the associated graph is a cycle with at least three vertices `'S'` stands for `series` and is also called a `polygon`

  - `'P'` – the associated graph is a dipole graph, a multigraph with two vertices and three or more edges. `'P'` stands for `parallel` and the node is called a `bond`.

  - `'Q'` – the associated graph has a single real edge. This trivial case is necessary to handle the graph that has only one edge.

  - `'R'` – the associated graph is a 3-vertex-connected graph that is not a cycle or dipole. `'R'` stands for `rigid`.

  The edges of the tree indicate the 2-vertex cuts of the graph.

  INPUT:

  - `G` – graph; if `G` is a `DiGraph`, the computation is done on the underlying `Graph` (i.e., ignoring edge orientation)

  - `check` – boolean (default: `True`); indicates whether `G` needs to be tested for biconnectivity

  See also

  - `sage.graphs.connectivity.spqr_tree()`

  - `is_biconnected()`

  - Wikipedia article SPQR_tree

  EXAMPLES:

  Example from the Wikipedia article SPQR_tree:

  Sage

      sage: from sage.graphs.connectivity import TriconnectivitySPQR
      sage: from sage.graphs.connectivity import spqr_tree_to_graph
      sage: G = Graph([(1, 2), (1, 4), (1, 8), (1, 12), (3, 4), (2, 3),
      ....: (2, 13), (3, 13), (4, 5), (4, 7), (5, 6), (5, 8), (5, 7), (6, 7),
      ....: (8, 11), (8, 9), (8, 12), (9, 10), (9, 11), (9, 12), (10, 12)])
      sage: tric = TriconnectivitySPQR(G)
      sage: T = tric.get_spqr_tree()
      sage: G.is_isomorphic(spqr_tree_to_graph(T))
      True

  Python

      >>> from sage.all import *
      >>> from sage.graphs.connectivity import TriconnectivitySPQR
      >>> from sage.graphs.connectivity import spqr_tree_to_graph
      >>> G = Graph([(Integer(1), Integer(2)), (Integer(1), Integer(4)), (Integer(1), Integer(8)), (Integer(1), Integer(12)), (Integer(3), Integer(4)), (Integer(2), Integer(3)),
      ... (Integer(2), Integer(13)), (Integer(3), Integer(13)), (Integer(4), Integer(5)), (Integer(4), Integer(7)), (Integer(5), Integer(6)), (Integer(5), Integer(8)), (Integer(5), Integer(7)), (Integer(6), Integer(7)),
      ... (Integer(8), Integer(11)), (Integer(8), Integer(9)), (Integer(8), Integer(12)), (Integer(9), Integer(10)), (Integer(9), Integer(11)), (Integer(9), Integer(12)), (Integer(10), Integer(12))])
      >>> tric = TriconnectivitySPQR(G)
      >>> T = tric.get_spqr_tree()
      >>> G.is_isomorphic(spqr_tree_to_graph(T))
      True

  An example from [Hopcroft1973]:

  Sage

      sage: G = Graph([(1, 2), (1, 4), (1, 8), (1, 12), (1, 13), (2, 3),
      ....: (2, 13), (3, 4), (3, 13), (4, 5), (4, 7), (5, 6), (5, 7), (5, 8),
      ....: (6, 7), (8, 9), (8, 11), (8, 12), (9, 10), (9, 11), (9, 12),
      ....: (10, 11), (10, 12)])
      sage: tric = TriconnectivitySPQR(G)
      sage: T = tric.get_spqr_tree()
      sage: G.is_isomorphic(spqr_tree_to_graph(T))
      True
      sage: tric.print_triconnected_components()
      Triconnected: [(8, 9, None), (9, 12, None), (9, 11, None), (8, 11, None), (10, 11, None), (9, 10, None), (10, 12, None), (8, 12, 'newVEdge0')]
      Bond: [(8, 12, None), (8, 12, 'newVEdge0'), (8, 12, 'newVEdge1')]
      Polygon: [(6, 7, None), (5, 6, None), (7, 5, 'newVEdge2')]
      Bond: [(7, 5, 'newVEdge2'), (5, 7, 'newVEdge3'), (5, 7, None)]
      Polygon: [(5, 7, 'newVEdge3'), (4, 7, None), (5, 4, 'newVEdge4')]
      Bond: [(5, 4, 'newVEdge4'), (4, 5, 'newVEdge5'), (4, 5, None)]
      Polygon: [(4, 5, 'newVEdge5'), (5, 8, None), (1, 4, 'newVEdge9'), (1, 8, 'newVEdge10')]
      Triconnected: [(1, 2, None), (2, 13, None), (1, 13, None), (3, 13, None), (2, 3, None), (1, 3, 'newVEdge7')]
      Polygon: [(1, 3, 'newVEdge7'), (3, 4, None), (1, 4, 'newVEdge8')]
      Bond: [(1, 4, None), (1, 4, 'newVEdge8'), (1, 4, 'newVEdge9')]
      Bond: [(1, 8, None), (1, 8, 'newVEdge10'), (1, 8, 'newVEdge11')]
      Polygon: [(8, 12, 'newVEdge1'), (1, 8, 'newVEdge11'), (1, 12, None)]

  [Python]

      >>> from sage.all import *
      >>> G = Graph([(Integer(1), Integer(2)), (Integer(1), Integer(4)), (Integer(1), Integer(8)), (Integer(1), Integer(12)), (Integer(1), Integer(13)), (Integer(2), Integer(3)),
      ... (Integer(2), Integer(13)), (Integer(3), Integer(4)), (Integer(3), Integer(13)), (Integer(4), Integer(5)), (Integer(4), Integer(7)), (Integer(5), Integer(6)), (Integer(5), Integer(7)), (Integer(5), Integer(8)),
      ... (Integer(6), Integer(7)), (Integer(8), Integer(9)), (Integer(8), Integer(11)), (Integer(8), Integer(12)), (Integer(9), Integer(10)), (Integer(9), Integer(11)), (Integer(9), Integer(12)),
      ... (Integer(10), Integer(11)), (Integer(10), Integer(12))])
      >>> tric = TriconnectivitySPQR(G)
      >>> T = tric.get_spqr_tree()
      >>> G.is_isomorphic(spqr_tree_to_graph(T))
      True
      >>> tric.print_triconnected_components()
      Triconnected: [(8, 9, None), (9, 12, None), (9, 11, None), (8, 11, None), (10, 11, None), (9, 10, None), (10, 12, None), (8, 12, 'newVEdge0')]
      Bond: [(8, 12, None), (8, 12, 'newVEdge0'), (8, 12, 'newVEdge1')]
      Polygon: [(6, 7, None), (5, 6, None), (7, 5, 'newVEdge2')]
      Bond: [(7, 5, 'newVEdge2'), (5, 7, 'newVEdge3'), (5, 7, None)]
      Polygon: [(5, 7, 'newVEdge3'), (4, 7, None), (5, 4, 'newVEdge4')]
      Bond: [(5, 4, 'newVEdge4'), (4, 5, 'newVEdge5'), (4, 5, None)]
      Polygon: [(4, 5, 'newVEdge5'), (5, 8, None), (1, 4, 'newVEdge9'), (1, 8, 'newVEdge10')]
      Triconnected: [(1, 2, None), (2, 13, None), (1, 13, None), (3, 13, None), (2, 3, None), (1, 3, 'newVEdge7')]
      Polygon: [(1, 3, 'newVEdge7'), (3, 4, None), (1, 4, 'newVEdge8')]
      Bond: [(1, 4, None), (1, 4, 'newVEdge8'), (1, 4, 'newVEdge9')]
      Bond: [(1, 8, None), (1, 8, 'newVEdge10'), (1, 8, 'newVEdge11')]
      Polygon: [(8, 12, 'newVEdge1'), (1, 8, 'newVEdge11'), (1, 12, None)]

  An example from [Gut2001]:

  Sage

      sage: G = Graph([(1, 2), (1, 4), (2, 3), (2, 5), (3, 4), (3, 5), (4, 5),
      ....: (4, 6), (5, 7), (5, 8), (5, 14), (6, 8), (7, 14), (8, 9), (8, 10),
      ....: (8, 11), (8, 12), (9, 10), (10, 13), (10, 14), (10, 15), (10, 16),
      ....: (11, 12), (11, 13), (12, 13), (14, 15), (14, 16), (15, 16)])
      sage: T = TriconnectivitySPQR(G).get_spqr_tree()
      sage: G.is_isomorphic(spqr_tree_to_graph(T))
      True

  Python

      >>> from sage.all import *
      >>> G = Graph([(Integer(1), Integer(2)), (Integer(1), Integer(4)), (Integer(2), Integer(3)), (Integer(2), Integer(5)), (Integer(3), Integer(4)), (Integer(3), Integer(5)), (Integer(4), Integer(5)),
      ... (Integer(4), Integer(6)), (Integer(5), Integer(7)), (Integer(5), Integer(8)), (Integer(5), Integer(14)), (Integer(6), Integer(8)), (Integer(7), Integer(14)), (Integer(8), Integer(9)), (Integer(8), Integer(10)),
      ... (Integer(8), Integer(11)), (Integer(8), Integer(12)), (Integer(9), Integer(10)), (Integer(10), Integer(13)), (Integer(10), Integer(14)), (Integer(10), Integer(15)), (Integer(10), Integer(16)),
      ... (Integer(11), Integer(12)), (Integer(11), Integer(13)), (Integer(12), Integer(13)), (Integer(14), Integer(15)), (Integer(14), Integer(16)), (Integer(15), Integer(16))])
      >>> T = TriconnectivitySPQR(G).get_spqr_tree()
      >>> G.is_isomorphic(spqr_tree_to_graph(T))
      True

  An example with multi-edges and accessing the triconnected components:

  Sage

      sage: G = Graph([(1, 2), (1, 5), (1, 5), (2, 3), (2, 3), (3, 4), (4, 5)], multiedges=True)
      sage: tric = TriconnectivitySPQR(G)
      sage: T = tric.get_spqr_tree()
      sage: G.is_isomorphic(spqr_tree_to_graph(T))
      True
      sage: tric.print_triconnected_components()
      Bond:  [(1, 5, None), (1, 5, None), (1, 5, 'newVEdge0')]
      Bond:  [(2, 3, None), (2, 3, None), (2, 3, 'newVEdge1')]
      Polygon:  [(4, 5, None), (1, 5, 'newVEdge0'), (3, 4, None), (2, 3, 'newVEdge1'), (1, 2, None)]

  [Python]

      >>> from sage.all import *
      >>> G = Graph([(Integer(1), Integer(2)), (Integer(1), Integer(5)), (Integer(1), Integer(5)), (Integer(2), Integer(3)), (Integer(2), Integer(3)), (Integer(3), Integer(4)), (Integer(4), Integer(5))], multiedges=True)
      >>> tric = TriconnectivitySPQR(G)
      >>> T = tric.get_spqr_tree()
      >>> G.is_isomorphic(spqr_tree_to_graph(T))
      True
      >>> tric.print_triconnected_components()
      Bond:  [(1, 5, None), (1, 5, None), (1, 5, 'newVEdge0')]
      Bond:  [(2, 3, None), (2, 3, None), (2, 3, 'newVEdge1')]
      Polygon:  [(4, 5, None), (1, 5, 'newVEdge0'), (3, 4, None), (2, 3, 'newVEdge1'), (1, 2, None)]

  An example of a triconnected graph:

  Sage

      sage: G = Graph([('a', 'b'), ('a', 'c'), ('a', 'd'), ('b', 'c'), ('b', 'd'), ('c', 'd')])
      sage: T = TriconnectivitySPQR(G).get_spqr_tree()
      sage: print(T.vertices(sort=True))
      [('R', Multi-graph on 4 vertices)]
      sage: G.is_isomorphic(spqr_tree_to_graph(T))
      True

  Python

      >>> from sage.all import *
      >>> G = Graph([('a', 'b'), ('a', 'c'), ('a', 'd'), ('b', 'c'), ('b', 'd'), ('c', 'd')])
      >>> T = TriconnectivitySPQR(G).get_spqr_tree()
      >>> print(T.vertices(sort=True))
      [('R', Multi-graph on 4 vertices)]
      >>> G.is_isomorphic(spqr_tree_to_graph(T))
      True

  An example of a directed graph with multi-edges:

  Sage

      sage: G = DiGraph([(1, 2), (2, 3), (3, 4), (4, 5), (1, 5), (5, 1)])
      sage: tric = TriconnectivitySPQR(G)
      sage: tric.print_triconnected_components()
      Bond:  [(1, 5, None), (5, 1, None), (1, 5, 'newVEdge0')]
      Polygon:  [(4, 5, None), (1, 5, 'newVEdge0'), (3, 4, None), (2, 3, None), (1, 2, None)]

  [Python]

      >>> from sage.all import *
      >>> G = DiGraph([(Integer(1), Integer(2)), (Integer(2), Integer(3)), (Integer(3), Integer(4)), (Integer(4), Integer(5)), (Integer(1), Integer(5)), (Integer(5), Integer(1))])
      >>> tric = TriconnectivitySPQR(G)
      >>> tric.print_triconnected_components()
      Bond:  [(1, 5, None), (5, 1, None), (1, 5, 'newVEdge0')]
      Polygon:  [(4, 5, None), (1, 5, 'newVEdge0'), (3, 4, None), (2, 3, None), (1, 2, None)]

  Edge labels are preserved by the construction:

  Sage

      sage: G = Graph([(0, 1, '01'), (0, 4, '04'), (1, 2, '12'), (1, 5, '15'),
      ....: (2, 3, '23'), (2, 6, '26'), (3, 7, '37'), (4, 5, '45'),
      ....: (5, 6, '56'), (6, 7, 67)])
      sage: T = TriconnectivitySPQR(G).get_spqr_tree()
      sage: H = spqr_tree_to_graph(T)
      sage: all(G.has_edge(e) for e in H.edge_iterator())
      True
      sage: all(H.has_edge(e) for e in G.edge_iterator())
      True

  Python

      >>> from sage.all import *
      >>> G = Graph([(Integer(0), Integer(1), '01'), (Integer(0), Integer(4), '04'), (Integer(1), Integer(2), '12'), (Integer(1), Integer(5), '15'),
      ... (Integer(2), Integer(3), '23'), (Integer(2), Integer(6), '26'), (Integer(3), Integer(7), '37'), (Integer(4), Integer(5), '45'),
      ... (Integer(5), Integer(6), '56'), (Integer(6), Integer(7), Integer(67))])
      >>> T = TriconnectivitySPQR(G).get_spqr_tree()
      >>> H = spqr_tree_to_graph(T)
      >>> all(G.has_edge(e) for e in H.edge_iterator())
      True
      >>> all(H.has_edge(e) for e in G.edge_iterator())
      True

  get_spqr_tree()

    Return an SPQR-tree representing the triconnected components of the graph.

    An SPQR-tree is a tree data structure used to represent the triconnected components of a biconnected (multi)graph and the 2-vertex cuts separating them. A node of a SPQR-tree, and the graph associated with it, can be one of the following four types:

    - `'S'` – the associated graph is a cycle with at least three vertices. `'S'` stands for `series`.

    - `'P'` – the associated graph is a dipole graph, a multigraph with two vertices and three or more edges. `'P'` stands for `parallel`.

    - `'Q'` – the associated graph has a single real edge. This trivial case is necessary to handle the graph that has only one edge.

    - `'R'` – the associated graph is a 3-connected graph that is not a cycle or dipole. `'R'` stands for `rigid`.

    The edges of the tree indicate the 2-vertex cuts of the graph.

    OUTPUT:

    `SPQR-tree` a tree whose vertices are labeled with the block’s type and the subgraph of three-blocks in the decomposition.

    EXAMPLES:

    Sage

        sage: from sage.graphs.connectivity import TriconnectivitySPQR
        sage: G = Graph(2)
        sage: for i in range(3):
        ....:     G.add_clique([0, 1, G.add_vertex(), G.add_vertex()])
        sage: tric = TriconnectivitySPQR(G)
        sage: Tree = tric.get_spqr_tree()
        sage: K4 = graphs.CompleteGraph(4)
        sage: all(u[1].is_isomorphic(K4) for u in Tree if u[0] == 'R')
        True
        sage: from sage.graphs.connectivity import spqr_tree_to_graph
        sage: G.is_isomorphic(spqr_tree_to_graph(Tree))
        True

        sage: G = Graph(2)
        sage: for i in range(3):
        ....:     G.add_path([0, G.add_vertex(), G.add_vertex(), 1])
        sage: tric = TriconnectivitySPQR(G)
        sage: Tree = tric.get_spqr_tree()
        sage: C4 = graphs.CycleGraph(4)
        sage: all(u[1].is_isomorphic(C4) for u in Tree if u[0] == 'S')
        True
        sage: G.is_isomorphic(spqr_tree_to_graph(Tree))
        True

        sage: G.allow_multiple_edges(True)
        sage: G.add_edges(G.edge_iterator())
        sage: tric = TriconnectivitySPQR(G)
        sage: Tree = tric.get_spqr_tree()
        sage: all(u[1].is_isomorphic(C4) for u in Tree if u[0] == 'S')
        True
        sage: G.is_isomorphic(spqr_tree_to_graph(Tree))
        True

        sage: G = graphs.CycleGraph(6)
        sage: tric = TriconnectivitySPQR(G)
        sage: Tree = tric.get_spqr_tree()
        sage: Tree.order()
        1
        sage: G.is_isomorphic(spqr_tree_to_graph(Tree))
        True
        sage: G.add_edge(0, 3)
        sage: tric = TriconnectivitySPQR(G)
        sage: Tree = tric.get_spqr_tree()
        sage: Tree.order()
        3
        sage: G.is_isomorphic(spqr_tree_to_graph(Tree))
        True

        sage: G = Graph([(0, 1)], multiedges=True)
        sage: tric = TriconnectivitySPQR(G)
        sage: Tree = tric.get_spqr_tree()
        sage: Tree.vertices(sort=True)
        [('Q', Multi-graph on 2 vertices)]
        sage: G.add_edge(0, 1)
        sage: Tree = TriconnectivitySPQR(G).get_spqr_tree()
        sage: Tree.vertices(sort=True)
        [('P', Multi-graph on 2 vertices)]

    Python

        >>> from sage.all import *
        >>> from sage.graphs.connectivity import TriconnectivitySPQR
        >>> G = Graph(Integer(2))
        >>> for i in range(Integer(3)):
        ...     G.add_clique([Integer(0), Integer(1), G.add_vertex(), G.add_vertex()])
        >>> tric = TriconnectivitySPQR(G)
        >>> Tree = tric.get_spqr_tree()
        >>> K4 = graphs.CompleteGraph(Integer(4))
        >>> all(u[Integer(1)].is_isomorphic(K4) for u in Tree if u[Integer(0)] == 'R')
        True
        >>> from sage.graphs.connectivity import spqr_tree_to_graph
        >>> G.is_isomorphic(spqr_tree_to_graph(Tree))
        True

        >>> G = Graph(Integer(2))
        >>> for i in range(Integer(3)):
        ...     G.add_path([Integer(0), G.add_vertex(), G.add_vertex(), Integer(1)])
        >>> tric = TriconnectivitySPQR(G)
        >>> Tree = tric.get_spqr_tree()
        >>> C4 = graphs.CycleGraph(Integer(4))
        >>> all(u[Integer(1)].is_isomorphic(C4) for u in Tree if u[Integer(0)] == 'S')
        True
        >>> G.is_isomorphic(spqr_tree_to_graph(Tree))
        True

        >>> G.allow_multiple_edges(True)
        >>> G.add_edges(G.edge_iterator())
        >>> tric = TriconnectivitySPQR(G)
        >>> Tree = tric.get_spqr_tree()
        >>> all(u[Integer(1)].is_isomorphic(C4) for u in Tree if u[Integer(0)] == 'S')
        True
        >>> G.is_isomorphic(spqr_tree_to_graph(Tree))
        True

        >>> G = graphs.CycleGraph(Integer(6))
        >>> tric = TriconnectivitySPQR(G)
        >>> Tree = tric.get_spqr_tree()
        >>> Tree.order()
        1
        >>> G.is_isomorphic(spqr_tree_to_graph(Tree))
        True
        >>> G.add_edge(Integer(0), Integer(3))
        >>> tric = TriconnectivitySPQR(G)
        >>> Tree = tric.get_spqr_tree()
        >>> Tree.order()
        3
        >>> G.is_isomorphic(spqr_tree_to_graph(Tree))
        True

        >>> G = Graph([(Integer(0), Integer(1))], multiedges=True)
        >>> tric = TriconnectivitySPQR(G)
        >>> Tree = tric.get_spqr_tree()
        >>> Tree.vertices(sort=True)
        [('Q', Multi-graph on 2 vertices)]
        >>> G.add_edge(Integer(0), Integer(1))
        >>> Tree = TriconnectivitySPQR(G).get_spqr_tree()
        >>> Tree.vertices(sort=True)
        [('P', Multi-graph on 2 vertices)]

  get_triconnected_components()

    Return the triconnected components as a list of tuples.

    Each component is represented as a tuple of the type of the component and the list of edges of the component.

    EXAMPLES:

    Sage

        sage: from sage.graphs.connectivity import TriconnectivitySPQR
        sage: G = Graph(2)
        sage: for i in range(3):
        ....:     G.add_path([0, G.add_vertex(), G.add_vertex(), 1])
        sage: tric = TriconnectivitySPQR(G)
        sage: tric.get_triconnected_components()
        [('Polygon', [(4, 5, None), (0, 4, None), (1, 5, None), (1, 0, 'newVEdge1')]),
        ('Polygon', [(6, 7, None), (0, 6, None), (1, 7, None), (1, 0, 'newVEdge3')]),
        ('Bond', [(1, 0, 'newVEdge1'), (1, 0, 'newVEdge3'), (1, 0, 'newVEdge4')]),
        ('Polygon', [(1, 3, None), (1, 0, 'newVEdge4'), (2, 3, None), (0, 2, None)])]

    Python

        >>> from sage.all import *
        >>> from sage.graphs.connectivity import TriconnectivitySPQR
        >>> G = Graph(Integer(2))
        >>> for i in range(Integer(3)):
        ...     G.add_path([Integer(0), G.add_vertex(), G.add_vertex(), Integer(1)])
        >>> tric = TriconnectivitySPQR(G)
        >>> tric.get_triconnected_components()
        [('Polygon', [(4, 5, None), (0, 4, None), (1, 5, None), (1, 0, 'newVEdge1')]),
        ('Polygon', [(6, 7, None), (0, 6, None), (1, 7, None), (1, 0, 'newVEdge3')]),
        ('Bond', [(1, 0, 'newVEdge1'), (1, 0, 'newVEdge3'), (1, 0, 'newVEdge4')]),
        ('Polygon', [(1, 3, None), (1, 0, 'newVEdge4'), (2, 3, None), (0, 2, None)])]

  print_triconnected_components()

    Print the type and list of edges of each component.

    EXAMPLES:

    An example from [Hopcroft1973]:

    Sage

        sage: from sage.graphs.connectivity import TriconnectivitySPQR
        sage: from sage.graphs.connectivity import spqr_tree_to_graph
        sage: G = Graph([(1, 2), (1, 4), (1, 8), (1, 12), (1, 13), (2, 3),
        ....: (2, 13), (3, 4), (3, 13), (4, 5), (4, 7), (5, 6), (5, 7), (5, 8),
        ....: (6, 7), (8, 9), (8, 11), (8, 12), (9, 10), (9, 11), (9, 12),
        ....: (10, 11), (10, 12)])
        sage: tric = TriconnectivitySPQR(G)
        sage: T = tric.get_spqr_tree()
        sage: G.is_isomorphic(spqr_tree_to_graph(T))
        True
        sage: tric.print_triconnected_components()
        Triconnected: [(8, 9, None), (9, 12, None), (9, 11, None), (8, 11, None), (10, 11, None), (9, 10, None), (10, 12, None), (8, 12, 'newVEdge0')]
        Bond: [(8, 12, None), (8, 12, 'newVEdge0'), (8, 12, 'newVEdge1')]
        Polygon: [(6, 7, None), (5, 6, None), (7, 5, 'newVEdge2')]
        Bond: [(7, 5, 'newVEdge2'), (5, 7, 'newVEdge3'), (5, 7, None)]
        Polygon: [(5, 7, 'newVEdge3'), (4, 7, None), (5, 4, 'newVEdge4')]
        Bond: [(5, 4, 'newVEdge4'), (4, 5, 'newVEdge5'), (4, 5, None)]
        Polygon: [(4, 5, 'newVEdge5'), (5, 8, None), (1, 4, 'newVEdge9'), (1, 8, 'newVEdge10')]
        Triconnected: [(1, 2, None), (2, 13, None), (1, 13, None), (3, 13, None), (2, 3, None), (1, 3, 'newVEdge7')]
        Polygon: [(1, 3, 'newVEdge7'), (3, 4, None), (1, 4, 'newVEdge8')]
        Bond: [(1, 4, None), (1, 4, 'newVEdge8'), (1, 4, 'newVEdge9')]
        Bond: [(1, 8, None), (1, 8, 'newVEdge10'), (1, 8, 'newVEdge11')]
        Polygon: [(8, 12, 'newVEdge1'), (1, 8, 'newVEdge11'), (1, 12, None)]

    Python

        >>> from sage.all import *
        >>> from sage.graphs.connectivity import TriconnectivitySPQR
        >>> from sage.graphs.connectivity import spqr_tree_to_graph
        >>> G = Graph([(Integer(1), Integer(2)), (Integer(1), Integer(4)), (Integer(1), Integer(8)), (Integer(1), Integer(12)), (Integer(1), Integer(13)), (Integer(2), Integer(3)),
        ... (Integer(2), Integer(13)), (Integer(3), Integer(4)), (Integer(3), Integer(13)), (Integer(4), Integer(5)), (Integer(4), Integer(7)), (Integer(5), Integer(6)), (Integer(5), Integer(7)), (Integer(5), Integer(8)),
        ... (Integer(6), Integer(7)), (Integer(8), Integer(9)), (Integer(8), Integer(11)), (Integer(8), Integer(12)), (Integer(9), Integer(10)), (Integer(9), Integer(11)), (Integer(9), Integer(12)),
        ... (Integer(10), Integer(11)), (Integer(10), Integer(12))])
        >>> tric = TriconnectivitySPQR(G)
        >>> T = tric.get_spqr_tree()
        >>> G.is_isomorphic(spqr_tree_to_graph(T))
        True
        >>> tric.print_triconnected_components()
        Triconnected: [(8, 9, None), (9, 12, None), (9, 11, None), (8, 11, None), (10, 11, None), (9, 10, None), (10, 12, None), (8, 12, 'newVEdge0')]
        Bond: [(8, 12, None), (8, 12, 'newVEdge0'), (8, 12, 'newVEdge1')]
        Polygon: [(6, 7, None), (5, 6, None), (7, 5, 'newVEdge2')]
        Bond: [(7, 5, 'newVEdge2'), (5, 7, 'newVEdge3'), (5, 7, None)]
        Polygon: [(5, 7, 'newVEdge3'), (4, 7, None), (5, 4, 'newVEdge4')]
        Bond: [(5, 4, 'newVEdge4'), (4, 5, 'newVEdge5'), (4, 5, None)]
        Polygon: [(4, 5, 'newVEdge5'), (5, 8, None), (1, 4, 'newVEdge9'), (1, 8, 'newVEdge10')]
        Triconnected: [(1, 2, None), (2, 13, None), (1, 13, None), (3, 13, None), (2, 3, None), (1, 3, 'newVEdge7')]
        Polygon: [(1, 3, 'newVEdge7'), (3, 4, None), (1, 4, 'newVEdge8')]
        Bond: [(1, 4, None), (1, 4, 'newVEdge8'), (1, 4, 'newVEdge9')]
        Bond: [(1, 8, None), (1, 8, 'newVEdge10'), (1, 8, 'newVEdge11')]
        Polygon: [(8, 12, 'newVEdge1'), (1, 8, 'newVEdge11'), (1, 12, None)]

sage.graphs.connectivity.biconnected_components(G, forbidden_vertices=None)

  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

  - `forbidden_vertices` – list (default: `None`); set of vertices to avoid during the search. This is equivalent to getting the biconnected components of the graph after the removal of the forbidden vertices.

  EXAMPLES:

  Sage

      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]

  Python

      >>> from sage.all import *
      >>> from sage.graphs.connectivity import biconnected_components
      >>> G = Graph({Integer(0): [Integer(1), Integer(2)], Integer(1): [Integer(0), Integer(2)], Integer(2): [Integer(0), Integer(1), Integer(3)], Integer(3): [Integer(2)]})
      >>> sorted(len(b) for b in biconnected_components(G))
      [2, 3]
      >>> sorted(len(b) for b in biconnected_components(Integer(2) * G))
      [2, 2, 3, 3]

  Check the behavior of parameter `forbidden_vertices`:

  Sage

      sage: G = graphs.CycleGraph(5)
      sage: len(G.biconnected_components())
      1
      sage: len(G.biconnected_components(forbidden_vertices=[0]))
      3

  [Python]

      >>> from sage.all import *
      >>> G = graphs.CycleGraph(Integer(5))
      >>> len(G.biconnected_components())
      1
      >>> len(G.biconnected_components(forbidden_vertices=[Integer(0)]))
      3

sage.graphs.connectivity.biconnected_components_subgraphs(G, forbidden_vertices=None, immutable=None)

  Return a list of biconnected components as graph objects.

  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

  - `forbidden_vertices` – list (default: `None`); set of vertices to avoid during the search. This is equivalent to getting the biconnected components subgraphs of the graph after the removal of the forbidden vertices.

  - `immutable` – boolean (default: `None`); whether to create a mutable/immutable graphs. `immutable=None` (default) means that the graph and the subgraphs of its connected components will behave the same way.

  EXAMPLES:

  Sage

      sage: from sage.graphs.connectivity import biconnected_components_subgraphs
      sage: G = Graph({0: [1, 2], 1: [0, 2], 2: [0, 1, 3], 3: [2]})
      sage: L = biconnected_components_subgraphs(G)
      sage: L
      [Subgraph of (): Graph on 2 vertices, Subgraph of (): Graph on 3 vertices]
      sage: L[0].edges()
      [(2, 3, None)]
      sage: L[1].edges()
      [(0, 1, None), (0, 2, None), (1, 2, None)]

  Python

      >>> from sage.all import *
      >>> from sage.graphs.connectivity import biconnected_components_subgraphs
      >>> G = Graph({Integer(0): [Integer(1), Integer(2)], Integer(1): [Integer(0), Integer(2)], Integer(2): [Integer(0), Integer(1), Integer(3)], Integer(3): [Integer(2)]})
      >>> L = biconnected_components_subgraphs(G)
      >>> L
      [Subgraph of (): Graph on 2 vertices, Subgraph of (): Graph on 3 vertices]
      >>> L[Integer(0)].edges()
      [(2, 3, None)]
      >>> L[Integer(1)].edges()
      [(0, 1, None), (0, 2, None), (1, 2, None)]

  Check the behavior of parameter `forbidden_vertices`:

  Sage

      sage: G = graphs.CycleGraph(5)
      sage: len(G.biconnected_components_subgraphs())
      1
      sage: len(G.biconnected_components_subgraphs(forbidden_vertices=[0]))
      3

  [Python]

      >>> from sage.all import *
      >>> G = graphs.CycleGraph(Integer(5))
      >>> len(G.biconnected_components_subgraphs())
      1
      >>> len(G.biconnected_components_subgraphs(forbidden_vertices=[Integer(0)]))
      3

sage.graphs.connectivity.blocks_and_cut_vertices(G, algorithm='Tarjan_Boost', sort=False, key=None, forbidden_vertices=None)

  Return the blocks and cut vertices of the graph.

  In the case of a digraph, this computation is done on the underlying graph.

  A cut vertex is one whose deletion increases the number of connected components. A block is a maximal induced subgraph which itself has no cut vertices. Two distinct blocks cannot overlap in more than a single cut vertex.

  INPUT:

  - `algorithm` – string (default: `'Tarjan_Boost'`); the algorithm to use among:

    - `'Tarjan_Boost'` – default; Tarjan’s algorithm (Boost implementation)

    - `'Tarjan_Sage'` – Tarjan’s algorithm (Sage implementation)

  - `sort` – boolean (default: `False`); whether to sort vertices inside the components and the list of cut vertices currently only available for ``’Tarjan_Sage’``

  - `key` – a function (default: `None`); a function that takes a vertex as its one argument and returns a value that can be used for comparisons in the sorting algorithm (we must have `sort=True`)

  - `forbidden_vertices` – list (default: `None`); set of vertices to avoid during the search

  OUTPUT: `(B, C)`, where `B` is a list of blocks - each is a list of vertices and the blocks are the corresponding induced subgraphs - and `C` is a list of cut vertices.

  ALGORITHM:

  > We implement the algorithm proposed by Tarjan in [Tarjan72]. The original version is recursive. We emulate the recursion using a stack.

  See also

  - `blocks_and_cuts_tree()`

  - `sage.graphs.base.boost_graph.blocks_and_cut_vertices()`

  - `is_biconnected()`

  - `bridges()`

  EXAMPLES:

  We construct a trivial example of a graph with one cut vertex:

  Sage

      sage: from sage.graphs.connectivity import blocks_and_cut_vertices
      sage: rings = graphs.CycleGraph(10)
      sage: rings.merge_vertices([0, 5])
      sage: blocks_and_cut_vertices(rings)
      ([[0, 1, 4, 2, 3], [0, 6, 9, 7, 8]], [0])
      sage: rings.blocks_and_cut_vertices()
      ([[0, 1, 4, 2, 3], [0, 6, 9, 7, 8]], [0])
      sage: B, C = blocks_and_cut_vertices(rings, algorithm='Tarjan_Sage', sort=True)
      sage: B, C
      ([[0, 1, 2, 3, 4], [0, 6, 7, 8, 9]], [0])
      sage: B2, C2 = blocks_and_cut_vertices(rings, algorithm='Tarjan_Sage', sort=False)
      sage: Set(map(Set, B)) == Set(map(Set, B2)) and set(C) == set(C2)
      True

  Python

      >>> from sage.all import *
      >>> from sage.graphs.connectivity import blocks_and_cut_vertices
      >>> rings = graphs.CycleGraph(Integer(10))
      >>> rings.merge_vertices([Integer(0), Integer(5)])
      >>> blocks_and_cut_vertices(rings)
      ([[0, 1, 4, 2, 3], [0, 6, 9, 7, 8]], [0])
      >>> rings.blocks_and_cut_vertices()
      ([[0, 1, 4, 2, 3], [0, 6, 9, 7, 8]], [0])
      >>> B, C = blocks_and_cut_vertices(rings, algorithm='Tarjan_Sage', sort=True)
      >>> B, C
      ([[0, 1, 2, 3, 4], [0, 6, 7, 8, 9]], [0])
      >>> B2, C2 = blocks_and_cut_vertices(rings, algorithm='Tarjan_Sage', sort=False)
      >>> Set(map(Set, B)) == Set(map(Set, B2)) and set(C) == set(C2)
      True

  The Petersen graph is biconnected, hence has no cut vertices:

  Sage

      sage: blocks_and_cut_vertices(graphs.PetersenGraph())
      ([[0, 1, 4, 5, 2, 6, 3, 7, 8, 9]], [])

  [Python]

      >>> from sage.all import *
      >>> blocks_and_cut_vertices(graphs.PetersenGraph())
      ([[0, 1, 4, 5, 2, 6, 3, 7, 8, 9]], [])

  Decomposing paths to pairs:

  Sage

      sage: g = graphs.PathGraph(4) + graphs.PathGraph(5)
      sage: blocks_and_cut_vertices(g)
      ([[2, 3], [1, 2], [0, 1], [7, 8], [6, 7], [5, 6], [4, 5]], [1, 2, 5, 6, 7])

  Python

      >>> from sage.all import *
      >>> g = graphs.PathGraph(Integer(4)) + graphs.PathGraph(Integer(5))
      >>> blocks_and_cut_vertices(g)
      ([[2, 3], [1, 2], [0, 1], [7, 8], [6, 7], [5, 6], [4, 5]], [1, 2, 5, 6, 7])

  A disconnected graph:

  Sage

      sage: g = Graph({1: {2: 28, 3: 10}, 2: {1: 10, 3: 16}, 4: {}, 5: {6: 3, 7: 10, 8: 4}})
      sage: blocks_and_cut_vertices(g)
      ([[1, 2, 3], [5, 6], [5, 7], [5, 8], [4]], [5])

  [Python]

      >>> from sage.all import *
      >>> g = Graph({Integer(1): {Integer(2): Integer(28), Integer(3): Integer(10)}, Integer(2): {Integer(1): Integer(10), Integer(3): Integer(16)}, Integer(4): {}, Integer(5): {Integer(6): Integer(3), Integer(7): Integer(10), Integer(8): Integer(4)}})
      >>> blocks_and_cut_vertices(g)
      ([[1, 2, 3], [5, 6], [5, 7], [5, 8], [4]], [5])

  A directed graph with Boost’s algorithm (Issue #25994):

  Sage

      sage: rings = graphs.CycleGraph(10)
      sage: rings.merge_vertices([0, 5])
      sage: rings = rings.to_directed()
      sage: blocks_and_cut_vertices(rings, algorithm='Tarjan_Boost')
      ([[0, 1, 4, 2, 3], [0, 6, 9, 7, 8]], [0])

  Python

      >>> from sage.all import *
      >>> rings = graphs.CycleGraph(Integer(10))
      >>> rings.merge_vertices([Integer(0), Integer(5)])
      >>> rings = rings.to_directed()
      >>> blocks_and_cut_vertices(rings, algorithm='Tarjan_Boost')
      ([[0, 1, 4, 2, 3], [0, 6, 9, 7, 8]], [0])

  Check the behavior of parameter `forbidden_vertices`:

  Sage

      sage: G = graphs.WindmillGraph(4, 3)
      sage: G.blocks_and_cut_vertices(sort=True)
      ([[0, 1, 2, 3], [0, 4, 5, 6], [0, 7, 8, 9]], [0])
      sage: G.blocks_and_cut_vertices(sort=True, algorithm='Tarjan_Boost', forbidden_vertices=[0])
      ([[1, 2, 3], [4, 5, 6], [7, 8, 9]], [])
      sage: G.blocks_and_cut_vertices(sort=True, algorithm='Tarjan_Sage', forbidden_vertices=[0])
      ([[1, 2, 3], [4, 5, 6], [7, 8, 9]], [])
      sage: G.blocks_and_cut_vertices(sort=True, algorithm='Tarjan_Boost', forbidden_vertices=[1])
      ([[0, 2, 3], [0, 4, 5, 6], [0, 7, 8, 9]], [0])
      sage: G.blocks_and_cut_vertices(sort=True, algorithm='Tarjan_Sage', forbidden_vertices=[1])
      ([[0, 2, 3], [0, 4, 5, 6], [0, 7, 8, 9]], [0])
      sage: G = graphs.PathGraph(3)
      sage: G.blocks_and_cut_vertices(sort=True, algorithm='Tarjan_Sage')
      ([[1, 2], [0, 1]], [1])
      sage: G.blocks_and_cut_vertices(sort=True, algorithm='Tarjan_Sage', forbidden_vertices=[0])
      ([[1, 2]], [])
      sage: G.blocks_and_cut_vertices(sort=True, algorithm='Tarjan_Sage', forbidden_vertices=[1])
      ([[0], [2]], [])
      sage: G.blocks_and_cut_vertices(sort=True, algorithm='Tarjan_Boost', forbidden_vertices=[1])
      ([[0], [2]], [])

  [Python]

      >>> from sage.all import *
      >>> G = graphs.WindmillGraph(Integer(4), Integer(3))
      >>> G.blocks_and_cut_vertices(sort=True)
      ([[0, 1, 2, 3], [0, 4, 5, 6], [0, 7, 8, 9]], [0])
      >>> G.blocks_and_cut_vertices(sort=True, algorithm='Tarjan_Boost', forbidden_vertices=[Integer(0)])
      ([[1, 2, 3], [4, 5, 6], [7, 8, 9]], [])
      >>> G.blocks_and_cut_vertices(sort=True, algorithm='Tarjan_Sage', forbidden_vertices=[Integer(0)])
      ([[1, 2, 3], [4, 5, 6], [7, 8, 9]], [])
      >>> G.blocks_and_cut_vertices(sort=True, algorithm='Tarjan_Boost', forbidden_vertices=[Integer(1)])
      ([[0, 2, 3], [0, 4, 5, 6], [0, 7, 8, 9]], [0])
      >>> G.blocks_and_cut_vertices(sort=True, algorithm='Tarjan_Sage', forbidden_vertices=[Integer(1)])
      ([[0, 2, 3], [0, 4, 5, 6], [0, 7, 8, 9]], [0])
      >>> G = graphs.PathGraph(Integer(3))
      >>> G.blocks_and_cut_vertices(sort=True, algorithm='Tarjan_Sage')
      ([[1, 2], [0, 1]], [1])
      >>> G.blocks_and_cut_vertices(sort=True, algorithm='Tarjan_Sage', forbidden_vertices=[Integer(0)])
      ([[1, 2]], [])
      >>> G.blocks_and_cut_vertices(sort=True, algorithm='Tarjan_Sage', forbidden_vertices=[Integer(1)])
      ([[0], [2]], [])
      >>> G.blocks_and_cut_vertices(sort=True, algorithm='Tarjan_Boost', forbidden_vertices=[Integer(1)])
      ([[0], [2]], [])

sage.graphs.connectivity.blocks_and_cuts_tree(G, forbidden_vertices=None)

  Return the blocks-and-cuts tree of `self`.

  This new graph has two different kinds of vertices, some representing the blocks (type B) and some other the cut vertices of the graph (type C).

  There is an edge between a vertex \(u\) of type B and a vertex \(v\) of type C if the cut-vertex corresponding to \(v\) is in the block corresponding to \(u\).

  The resulting graph is a tree, with the additional characteristic property that the distance between two leaves is even. When `self` is not connected, the resulting graph is a forest.

  When `self` is biconnected, the tree is reduced to a single node of type \(B\).

  We referred to [HarPri] and [Gallai] for blocks and cuts tree.

  INPUT:

  - `G` – a Sage graph

  - `forbidden_vertices` – list (default: `None`); set of vertices to avoid during the search. This is equivalent to getting the blocks and cut tree of a graph in which the forbidden vertices have been removed.

  See also

  - `blocks_and_cut_vertices()`

  - `is_biconnected()`

  EXAMPLES:

  Sage

      sage: from sage.graphs.connectivity import blocks_and_cuts_tree
      sage: T = blocks_and_cuts_tree(graphs.KrackhardtKiteGraph()); T
      Graph on 5 vertices
      sage: T.is_isomorphic(graphs.PathGraph(5))
      True
      sage: from sage.graphs.connectivity import blocks_and_cuts_tree
      sage: T = graphs.KrackhardtKiteGraph().blocks_and_cuts_tree(); T
      Graph on 5 vertices

  Python

      >>> from sage.all import *
      >>> from sage.graphs.connectivity import blocks_and_cuts_tree
      >>> T = blocks_and_cuts_tree(graphs.KrackhardtKiteGraph()); T
      Graph on 5 vertices
      >>> T.is_isomorphic(graphs.PathGraph(Integer(5)))
      True
      >>> from sage.graphs.connectivity import blocks_and_cuts_tree
      >>> T = graphs.KrackhardtKiteGraph().blocks_and_cuts_tree(); T
      Graph on 5 vertices

  The distance between two leaves is even:

  Sage

      sage: T = blocks_and_cuts_tree(graphs.RandomTree(40))
      sage: T.is_tree()
      True
      sage: leaves = [v for v in T if T.degree(v) == 1]
      sage: all(T.distance(u,v) % 2 == 0 for u in leaves for v in leaves)
      True

  [Python]

      >>> from sage.all import *
      >>> T = blocks_and_cuts_tree(graphs.RandomTree(Integer(40)))
      >>> T.is_tree()
      True
      >>> leaves = [v for v in T if T.degree(v) == Integer(1)]
      >>> all(T.distance(u,v) % Integer(2) == Integer(0) for u in leaves for v in leaves)
      True

  The tree of a biconnected graph has a single vertex, of type \(B\):

  Sage

      sage: T = blocks_and_cuts_tree(graphs.PetersenGraph())
      sage: T.vertices(sort=True)
      [('B', (0, 1, 4, 5, 2, 6, 3, 7, 8, 9))]

  Python

      >>> from sage.all import *
      >>> T = blocks_and_cuts_tree(graphs.PetersenGraph())
      >>> T.vertices(sort=True)
      [('B', (0, 1, 4, 5, 2, 6, 3, 7, 8, 9))]

  Check the behavior of parameter `forbidden_vertices`:

  Sage

      sage: G = graphs.CycleGraph(5)
      sage: G.blocks_and_cut_vertices()
      ([[0, 1, 4, 2, 3]], [])
      sage: G.blocks_and_cuts_tree()
      Graph on 1 vertex
      sage: G.blocks_and_cut_vertices(forbidden_vertices=[0])
      ([[3, 4], [2, 3], [1, 2]], [2, 3])
      sage: G.blocks_and_cuts_tree(forbidden_vertices=[0])
      Graph on 5 vertices

  [Python]

      >>> from sage.all import *
      >>> G = graphs.CycleGraph(Integer(5))
      >>> G.blocks_and_cut_vertices()
      ([[0, 1, 4, 2, 3]], [])
      >>> G.blocks_and_cuts_tree()
      Graph on 1 vertex
      >>> G.blocks_and_cut_vertices(forbidden_vertices=[Integer(0)])
      ([[3, 4], [2, 3], [1, 2]], [2, 3])
      >>> G.blocks_and_cuts_tree(forbidden_vertices=[Integer(0)])
      Graph on 5 vertices

sage.graphs.connectivity.bridges(G, labels=True)

  Return an iterator over the bridges (or cut edges).

  A bridge is an edge whose deletion disconnects the undirected graph. A disconnected graph has no bridge.

  INPUT:

  - `labels` – boolean (default: `True`); if `False`, each bridge is a tuple \((u, v)\) of vertices

  EXAMPLES:

  Sage

      sage: from sage.graphs.connectivity import bridges
      sage: from sage.graphs.connectivity import is_connected
      sage: g = 2 * graphs.PetersenGraph()
      sage: g.add_edge(1, 10)
      sage: is_connected(g)
      True
      sage: list(bridges(g))
      [(1, 10, None)]
      sage: list(g.bridges())
      [(1, 10, None)]

  Python

      >>> from sage.all import *
      >>> from sage.graphs.connectivity import bridges
      >>> from sage.graphs.connectivity import is_connected
      >>> g = Integer(2) * graphs.PetersenGraph()
      >>> g.add_edge(Integer(1), Integer(10))
      >>> is_connected(g)
      True
      >>> list(bridges(g))
      [(1, 10, None)]
      >>> list(g.bridges())
      [(1, 10, None)]

  Every edge of a tree is a bridge:

  Sage

      sage: g = graphs.RandomTree(100)
      sage: sum(1 for _ in g.bridges()) == 99
      True

  [Python]

      >>> from sage.all import *
      >>> g = graphs.RandomTree(Integer(100))
      >>> sum(Integer(1) for _ in g.bridges()) == Integer(99)
      True

sage.graphs.connectivity.cleave(G, cut_vertices=None, virtual_edges=True, solver=None, verbose=0, integrality_tolerance=0.001)

  Return the connected subgraphs separated by the input vertex cut.

  Given a connected (multi)graph \(G\) and a vertex cut \(X\), this method computes the list of subgraphs of \(G\) induced by each connected component \(c\) of \(G\setminus X\) plus \(X\), i.e., \(G[c\cup X]\).

  INPUT:

  - `G` – a Graph

  - `cut_vertices` – iterable container of vertices (default: `None`); a set of vertices representing a vertex cut of `G`. If no vertex cut is given, the method will compute one via a call to `vertex_connectivity()`.

  - `virtual_edges` – boolean (default: `True`); whether to add virtual edges to the sides of the cut or not. A virtual edge is an edge between a pair of vertices of the cut that are not connected by an edge in `G`.

  - `solver` – string (default: `None`); specifies a Mixed Integer Linear Programming (MILP) solver to be used. If set to `None`, the default one is used. For more information on MILP solvers and which default solver is used, see the method `solve` of the class `MixedIntegerLinearProgram`.

  - `verbose` – integer (default: 0); sets the level of verbosity. Set to 0 by default, which means quiet.

  - `integrality_tolerance` – float; parameter for use with MILP solvers over an inexact base ring; see `MixedIntegerLinearProgram.get_values()`.

  OUTPUT: a triple \((S, C, f)\), where

  - \(S\) is a list of the graphs that are sides of the vertex cut.

  - \(C\) is the graph of the cocycles. For each pair of vertices of the cut, if there exists an edge between them, \(C\) has one copy of each edge connecting them in `G` per sides of the cut plus one extra copy. Furthermore, when `virtual_edges == True`, if a pair of vertices of the cut is not connected by an edge in `G`, then it has one virtual edge between them per sides of the cut.

  - \(f\) is the complement of the subgraph of `G` induced by the vertex cut. Hence, its vertex set is the vertex cut, and its edge set is the set of virtual edges (i.e., edges between pairs of vertices of the cut that are not connected by an edge in `G`). When `virtual_edges == False`, the edge set is empty.

  EXAMPLES:

  If there is an edge between cut vertices:

  Sage

      sage: from sage.graphs.connectivity import cleave
      sage: G = Graph(2)
      sage: for _ in range(3):
      ....:     G.add_clique([0, 1, G.add_vertex(), G.add_vertex()])
      sage: S1,C1,f1 = cleave(G, cut_vertices=[0, 1])
      sage: [g.order() for g in S1]
      [4, 4, 4]
      sage: C1.order(), C1.size()
      (2, 4)
      sage: f1.vertices(sort=True), f1.edges(sort=True)
      ([0, 1], [])

  Python

      >>> from sage.all import *
      >>> from sage.graphs.connectivity import cleave
      >>> G = Graph(Integer(2))
      >>> for _ in range(Integer(3)):
      ...     G.add_clique([Integer(0), Integer(1), G.add_vertex(), G.add_vertex()])
      >>> S1,C1,f1 = cleave(G, cut_vertices=[Integer(0), Integer(1)])
      >>> [g.order() for g in S1]
      [4, 4, 4]
      >>> C1.order(), C1.size()
      (2, 4)
      >>> f1.vertices(sort=True), f1.edges(sort=True)
      ([0, 1], [])

  If `virtual_edges == False` and there is an edge between cut vertices:

  Sage

      sage: G.subgraph([0, 1]).complement() == Graph([[0, 1], []])
      True
      sage: S2,C2,f2 = cleave(G, cut_vertices=[0, 1], virtual_edges=False)
      sage: (S1 == S2, C1 == C2, f1 == f2)
      (True, True, True)

  [Python]

      >>> from sage.all import *
      >>> G.subgraph([Integer(0), Integer(1)]).complement() == Graph([[Integer(0), Integer(1)], []])
      True
      >>> S2,C2,f2 = cleave(G, cut_vertices=[Integer(0), Integer(1)], virtual_edges=False)
      >>> (S1 == S2, C1 == C2, f1 == f2)
      (True, True, True)

  If cut vertices doesn’t have edge between them:

  Sage

      sage: G.delete_edge(0, 1)
      sage: S1,C1,f1 = cleave(G, cut_vertices=[0, 1])
      sage: [g.order() for g in S1]
      [4, 4, 4]
      sage: C1.order(), C1.size()
      (2, 3)
      sage: f1.vertices(sort=True), f1.edges(sort=True)
      ([0, 1], [(0, 1, None)])

  Python

      >>> from sage.all import *
      >>> G.delete_edge(Integer(0), Integer(1))
      >>> S1,C1,f1 = cleave(G, cut_vertices=[Integer(0), Integer(1)])
      >>> [g.order() for g in S1]
      [4, 4, 4]
      >>> C1.order(), C1.size()
      (2, 3)
      >>> f1.vertices(sort=True), f1.edges(sort=True)
      ([0, 1], [(0, 1, None)])

  If `virtual_edges == False` and the cut vertices are not connected by an edge:

  Sage

      sage: G.subgraph([0, 1]).complement() == Graph([[0, 1], []])
      False
      sage: S2,C2,f2 = cleave(G, cut_vertices=[0, 1], virtual_edges=False)
      sage: [g.order() for g in S2]
      [4, 4, 4]
      sage: C2.order(), C2.size()
      (2, 0)
      sage: f2.vertices(sort=True), f2.edges(sort=True)
      ([0, 1], [])
      sage: (S1 == S2, C1 == C2, f1 == f2)
      (False, False, False)

  [Python]

      >>> from sage.all import *
      >>> G.subgraph([Integer(0), Integer(1)]).complement() == Graph([[Integer(0), Integer(1)], []])
      False
      >>> S2,C2,f2 = cleave(G, cut_vertices=[Integer(0), Integer(1)], virtual_edges=False)
      >>> [g.order() for g in S2]
      [4, 4, 4]
      >>> C2.order(), C2.size()
      (2, 0)
      >>> f2.vertices(sort=True), f2.edges(sort=True)
      ([0, 1], [])
      >>> (S1 == S2, C1 == C2, f1 == f2)
      (False, False, False)

  If \(G\) is a biconnected multigraph:

  Sage

      sage: G = graphs.CompleteBipartiteGraph(2, 3)
      sage: G.add_edge(2, 3)
      sage: G.allow_multiple_edges(True)
      sage: G.add_edges(G.edge_iterator())
      sage: G.add_edges([(0, 1), (0, 1), (0, 1)])
      sage: S,C,f = cleave(G, cut_vertices=[0, 1])
      sage: for g in S:
      ....:     print(g.edges(sort=True, labels=0))
      [(0, 1), (0, 1), (0, 1), (0, 2), (0, 2), (0, 3), (0, 3), (1, 2), (1, 2), (1, 3), (1, 3), (2, 3), (2, 3)]
      [(0, 1), (0, 1), (0, 1), (0, 4), (0, 4), (1, 4), (1, 4)]

  Python

      >>> from sage.all import *
      >>> G = graphs.CompleteBipartiteGraph(Integer(2), Integer(3))
      >>> G.add_edge(Integer(2), Integer(3))
      >>> G.allow_multiple_edges(True)
      >>> G.add_edges(G.edge_iterator())
      >>> G.add_edges([(Integer(0), Integer(1)), (Integer(0), Integer(1)), (Integer(0), Integer(1))])
      >>> S,C,f = cleave(G, cut_vertices=[Integer(0), Integer(1)])
      >>> for g in S:
      ...     print(g.edges(sort=True, labels=Integer(0)))
      [(0, 1), (0, 1), (0, 1), (0, 2), (0, 2), (0, 3), (0, 3), (1, 2), (1, 2), (1, 3), (1, 3), (2, 3), (2, 3)]
      [(0, 1), (0, 1), (0, 1), (0, 4), (0, 4), (1, 4), (1, 4)]

sage.graphs.connectivity.connected_component_containing_vertex(G, vertex, sort=False, key=None, forbidden_vertices=None)

  Return a list of the vertices connected to vertex.

  INPUT:

  - `G` – the input graph

  - `vertex` – the vertex to search for

  - `sort` – boolean (default: `False`); if `True`, vertices inside the component are sorted according to the default ordering

  - `key` – a function (default: `None`); a function that takes a vertex as its one argument and returns a value that can be used for comparisons in the sorting algorithm (we must have `sort=True`)

  - `forbidden_vertices` – list (default: `None`); set of vertices to avoid during the search. The start `vertex` cannot be in this set.

  EXAMPLES:

  Sage

      sage: from sage.graphs.connectivity import connected_component_containing_vertex
      sage: G = Graph({0: [1, 3], 1: [2], 2: [3], 4: [5, 6], 5: [6]})
      sage: connected_component_containing_vertex(G, 0, sort=True)
      [0, 1, 2, 3]
      sage: G.connected_component_containing_vertex(0, sort=True)
      [0, 1, 2, 3]
      sage: G.connected_component_containing_vertex(0, sort=True, forbidden_vertices=[1, 3])
      [0]
      sage: D = DiGraph({0: [1, 3], 1: [2], 2: [3], 4: [5, 6], 5: [6]})
      sage: connected_component_containing_vertex(D, 0, sort=True)
      [0, 1, 2, 3]
      sage: connected_component_containing_vertex(D, 0, sort=True, key=lambda x: -x)
      [3, 2, 1, 0]

  Python

      >>> from sage.all import *
      >>> from sage.graphs.connectivity import connected_component_containing_vertex
      >>> G = Graph({Integer(0): [Integer(1), Integer(3)], Integer(1): [Integer(2)], Integer(2): [Integer(3)], Integer(4): [Integer(5), Integer(6)], Integer(5): [Integer(6)]})
      >>> connected_component_containing_vertex(G, Integer(0), sort=True)
      [0, 1, 2, 3]
      >>> G.connected_component_containing_vertex(Integer(0), sort=True)
      [0, 1, 2, 3]
      >>> G.connected_component_containing_vertex(Integer(0), sort=True, forbidden_vertices=[Integer(1), Integer(3)])
      [0]
      >>> D = DiGraph({Integer(0): [Integer(1), Integer(3)], Integer(1): [Integer(2)], Integer(2): [Integer(3)], Integer(4): [Integer(5), Integer(6)], Integer(5): [Integer(6)]})
      >>> connected_component_containing_vertex(D, Integer(0), sort=True)
      [0, 1, 2, 3]
      >>> connected_component_containing_vertex(D, Integer(0), sort=True, key=lambda x: -x)
      [3, 2, 1, 0]

sage.graphs.connectivity.connected_components(G, sort=False, key=None, forbidden_vertices=None)

  Return the list of connected components.

  This returns a list of lists of vertices, each list representing a connected component. The list is ordered from largest to smallest component.

  INPUT:

  - `G` – the input graph

  - `sort` – boolean (default: `False`); if `True`, vertices inside each component are sorted according to the default ordering

  - `key` – a function (default: `None`); a function that takes a vertex as its one argument and returns a value that can be used for comparisons in the sorting algorithm (we must have `sort=True`)

  - `forbidden_vertices` – list (default: `None`); set of vertices to avoid during the search

  EXAMPLES:

  Sage

      sage: from sage.graphs.connectivity import connected_components
      sage: G = Graph({0: [1, 3], 1: [2], 2: [3], 4: [5, 6], 5: [6]})
      sage: connected_components(G, sort=True)
      [[0, 1, 2, 3], [4, 5, 6]]
      sage: G.connected_components(sort=True)
      [[0, 1, 2, 3], [4, 5, 6]]
      sage: D = DiGraph({0: [1, 3], 1: [2], 2: [3], 4: [5, 6], 5: [6]})
      sage: connected_components(D, sort=True)
      [[0, 1, 2, 3], [4, 5, 6]]
      sage: connected_components(D, sort=True, key=lambda x: -x)
      [[3, 2, 1, 0], [6, 5, 4]]

  Python

      >>> from sage.all import *
      >>> from sage.graphs.connectivity import connected_components
      >>> G = Graph({Integer(0): [Integer(1), Integer(3)], Integer(1): [Integer(2)], Integer(2): [Integer(3)], Integer(4): [Integer(5), Integer(6)], Integer(5): [Integer(6)]})
      >>> connected_components(G, sort=True)
      [[0, 1, 2, 3], [4, 5, 6]]
      >>> G.connected_components(sort=True)
      [[0, 1, 2, 3], [4, 5, 6]]
      >>> D = DiGraph({Integer(0): [Integer(1), Integer(3)], Integer(1): [Integer(2)], Integer(2): [Integer(3)], Integer(4): [Integer(5), Integer(6)], Integer(5): [Integer(6)]})
      >>> connected_components(D, sort=True)
      [[0, 1, 2, 3], [4, 5, 6]]
      >>> connected_components(D, sort=True, key=lambda x: -x)
      [[3, 2, 1, 0], [6, 5, 4]]

  Connected components in a graph with forbidden vertices:

  Sage

      sage: G = graphs.PathGraph(5)
      sage: connected_components(G, sort=True, forbidden_vertices=[2])
      [[0, 1], [3, 4]]
      sage: connected_components(G, sort=True,
      ....:     forbidden_vertices=G.neighbor_iterator(2, closed=True))
      [[0], [4]]

  [Python]

      >>> from sage.all import *
      >>> G = graphs.PathGraph(Integer(5))
      >>> connected_components(G, sort=True, forbidden_vertices=[Integer(2)])
      [[0, 1], [3, 4]]
      >>> connected_components(G, sort=True,
      ...     forbidden_vertices=G.neighbor_iterator(Integer(2), closed=True))
      [[0], [4]]

sage.graphs.connectivity.connected_components_number(G, forbidden_vertices=None)

  alias of `number_of_connected_components()`.

sage.graphs.connectivity.connected_components_sizes(G, forbidden_vertices=None)

  Return the sizes of the connected components as a list.

  The list is sorted from largest to lower values.

  INPUT:

  - `G` – the input graph

  - `forbidden_vertices` – list (default: `None`); set of vertices to avoid during the search

  EXAMPLES:

  Sage

      sage: from sage.graphs.connectivity import connected_components_sizes
      sage: for x in graphs(3):
      ....:     print(connected_components_sizes(x))
      [1, 1, 1]
      [2, 1]
      [3]
      [3]
      sage: for x in graphs(3):
      ....:     print(x.connected_components_sizes())
      [1, 1, 1]
      [2, 1]
      [3]
      [3]
      sage: G = graphs.PathGraph(5)
      sage: G.connected_components_sizes()
      [5]
      sage: G.connected_components_sizes(forbidden_vertices=[1])
      [3, 1]
      sage: G.connected_components_sizes(forbidden_vertices=[1, 3])
      [1, 1, 1]

  Python

      >>> from sage.all import *
      >>> from sage.graphs.connectivity import connected_components_sizes
      >>> for x in graphs(Integer(3)):
      ...     print(connected_components_sizes(x))
      [1, 1, 1]
      [2, 1]
      [3]
      [3]
      >>> for x in graphs(Integer(3)):
      ...     print(x.connected_components_sizes())
      [1, 1, 1]
      [2, 1]
      [3]
      [3]
      >>> G = graphs.PathGraph(Integer(5))
      >>> G.connected_components_sizes()
      [5]
      >>> G.connected_components_sizes(forbidden_vertices=[Integer(1)])
      [3, 1]
      >>> G.connected_components_sizes(forbidden_vertices=[Integer(1), Integer(3)])
      [1, 1, 1]

sage.graphs.connectivity.connected_components_subgraphs(G, forbidden_vertices=None, immutable=None)

  Return a list of connected components as graph objects.

  INPUT:

  - `G` – the input graph

  - `forbidden_vertices` – list (default: `None`); set of vertices to avoid during the search

  - `immutable` – boolean (default: `None`); whether to create a mutable/immutable subgraphs. `immutable=None` (default) means that the graph and the subgraphs of its connected components will behave the same way.

  EXAMPLES:

  Sage

      sage: from sage.graphs.connectivity import connected_components_subgraphs
      sage: G = Graph({0: [1, 3], 1: [2], 2: [3], 4: [5, 6], 5: [6]})
      sage: L = connected_components_subgraphs(G)
      sage: graphs_list.show_graphs(L)                                                # needs sage.plot
      sage: L = connected_components_subgraphs(G, forbidden_vertices=[1, 3])
      sage: graphs_list.show_graphs(L)                                                # needs sage.plot
      sage: D = DiGraph({0: [1, 3], 1: [2], 2: [3], 4: [5, 6], 5: [6]})
      sage: L = connected_components_subgraphs(D)
      sage: graphs_list.show_graphs(L)                                                # needs sage.plot
      sage: L = D.connected_components_subgraphs()
      sage: graphs_list.show_graphs(L)                                                # needs sage.plot

  Python

      >>> from sage.all import *
      >>> from sage.graphs.connectivity import connected_components_subgraphs
      >>> G = Graph({Integer(0): [Integer(1), Integer(3)], Integer(1): [Integer(2)], Integer(2): [Integer(3)], Integer(4): [Integer(5), Integer(6)], Integer(5): [Integer(6)]})
      >>> L = connected_components_subgraphs(G)
      >>> graphs_list.show_graphs(L)                                                # needs sage.plot
      >>> L = connected_components_subgraphs(G, forbidden_vertices=[Integer(1), Integer(3)])
      >>> graphs_list.show_graphs(L)                                                # needs sage.plot
      >>> D = DiGraph({Integer(0): [Integer(1), Integer(3)], Integer(1): [Integer(2)], Integer(2): [Integer(3)], Integer(4): [Integer(5), Integer(6)], Integer(5): [Integer(6)]})
      >>> L = connected_components_subgraphs(D)
      >>> graphs_list.show_graphs(L)                                                # needs sage.plot
      >>> L = D.connected_components_subgraphs()
      >>> graphs_list.show_graphs(L)                                                # needs sage.plot

sage.graphs.connectivity.edge_connectivity(G, value_only=True, implementation=None, use_edge_labels=False, vertices=False, solver=None, verbose=0, integrality_tolerance=0.001)

  Return the edge connectivity of the graph.

  For more information, see the Wikipedia article Connectivity_(graph_theory).

  Note

  When the graph is a directed graph, this method actually computes the strong connectivity, (i.e. a directed graph is strongly \(k\)-connected if there are \(k\) disjoint paths between any two vertices \(u, v\)). If you do not want to consider strong connectivity, the best is probably to convert your `DiGraph` object to a `Graph` object, and compute the connectivity of this other graph.

  INPUT:

  - `G` – the input Sage (Di)Graph

  - `value_only` – boolean (default: `True`)

    - When set to `True` (default), only the value is returned.

    - When set to `False`, both the value and a minimum vertex cut are returned.

  - `implementation` – string (default: `None`); selects an implementation:

    - `None` – default; selects the best implementation available

    - `'boost'` – use the Boost graph library (which is much more efficient). It is not available when `edge_labels=True`, and it is unreliable for directed graphs (see Issue #18753).

    - `'Sage'` – use Sage’s implementation based on integer linear programming

  - `use_edge_labels` – boolean (default: `False`)

    - When set to `True`, computes a weighted minimum cut where each edge has a weight defined by its label. (If an edge has no label, \(1\) is assumed.). Implies `boost` = `False`.

    - When set to `False`, each edge has weight \(1\).

  - `vertices` – boolean (default: `False`)

    - When set to `True`, also returns the two sets of vertices that are disconnected by the cut. Implies `value_only=False`.

  - `solver` – string (default: `None`); specifies a Mixed Integer Linear Programming (MILP) solver to be used. If set to `None`, the default one is used. For more information on MILP solvers and which default solver is used, see the method `solve` of the class `MixedIntegerLinearProgram`.

  - `verbose` – integer (default: 0); sets the level of verbosity. Set to 0 by default, which means quiet.

  - `integrality_tolerance` – float; parameter for use with MILP solvers over an inexact base ring; see `MixedIntegerLinearProgram.get_values()`.

  EXAMPLES:

  A basic application on the PappusGraph:

  Sage

      sage: from sage.graphs.connectivity import edge_connectivity
      sage: g = graphs.PappusGraph()
      sage: edge_connectivity(g)
      3
      sage: g.edge_connectivity()
      3

  Python

      >>> from sage.all import *
      >>> from sage.graphs.connectivity import edge_connectivity
      >>> g = graphs.PappusGraph()
      >>> edge_connectivity(g)
      3
      >>> g.edge_connectivity()
      3

  The edge connectivity of a complete graph is its minimum degree, and one of the two parts of the bipartition is reduced to only one vertex. The graph of the cut edges is isomorphic to a Star graph:

  Sage

      sage: g = graphs.CompleteGraph(5)
      sage: [ value, edges, [ setA, setB ]] = edge_connectivity(g,vertices=True)
      sage: value
      4
      sage: len(setA) == 1 or len(setB) == 1
      True
      sage: cut = Graph()
      sage: cut.add_edges(edges)
      sage: cut.is_isomorphic(graphs.StarGraph(4))
      True

  [Python]

      >>> from sage.all import *
      >>> g = graphs.CompleteGraph(Integer(5))
      >>> [ value, edges, [ setA, setB ]] = edge_connectivity(g,vertices=True)
      >>> value
      4
      >>> len(setA) == Integer(1) or len(setB) == Integer(1)
      True
      >>> cut = Graph()
      >>> cut.add_edges(edges)
      >>> cut.is_isomorphic(graphs.StarGraph(Integer(4)))
      True

  Even if obviously in any graph we know that the edge connectivity is less than the minimum degree of the graph:

  Sage

      sage: g = graphs.RandomGNP(10,.3)
      sage: min(g.degree()) >= edge_connectivity(g)
      True

  Python

      >>> from sage.all import *
      >>> g = graphs.RandomGNP(Integer(10),RealNumber('.3'))
      >>> min(g.degree()) >= edge_connectivity(g)
      True

  If we build a tree then assign to its edges a random value, the minimum cut will be the edge with minimum value:

  Sage

      sage: tree = graphs.RandomTree(10)
      sage: for u,v in tree.edge_iterator(labels=None):
      ....:      tree.set_edge_label(u, v, random())
      sage: minimum = min(tree.edge_labels())
      sage: [_, [(_, _, l)]] = edge_connectivity(tree, value_only=False,              # needs sage.numerical.mip
      ....:                                      use_edge_labels=True)
      sage: l == minimum                                                              # needs sage.numerical.mip
      True

  [Python]

      >>> from sage.all import *
      >>> tree = graphs.RandomTree(Integer(10))
      >>> for u,v in tree.edge_iterator(labels=None):
      ...      tree.set_edge_label(u, v, random())
      >>> minimum = min(tree.edge_labels())
      >>> [_, [(_, _, l)]] = edge_connectivity(tree, value_only=False,              # needs sage.numerical.mip
      ...                                      use_edge_labels=True)
      >>> l == minimum                                                              # needs sage.numerical.mip
      True

  When `value_only=True` and `implementation="sage"`, this function is optimized for small connectivity values and does not need to build a linear program.

  It is the case for graphs which are not connected

  Sage

      sage: g = 2 * graphs.PetersenGraph()
      sage: edge_connectivity(g, implementation='sage')
      0.0

  Python

      >>> from sage.all import *
      >>> g = Integer(2) * graphs.PetersenGraph()
      >>> edge_connectivity(g, implementation='sage')
      0.0

  For directed graphs, the strong connectivity is tested through the dedicated function:

  Sage

      sage: g = digraphs.ButterflyGraph(3)
      sage: edge_connectivity(g, implementation='sage')
      0.0

  [Python]

      >>> from sage.all import *
      >>> g = digraphs.ButterflyGraph(Integer(3))
      >>> edge_connectivity(g, implementation='sage')
      0.0

  We check that the result with Boost is the same as the result without Boost:

  Sage

      sage: g = graphs.RandomGNP(15, .3)
      sage: (edge_connectivity(g, implementation='boost')                             # needs sage.numerical.mip
      ....:    == edge_connectivity(g, implementation='sage'))
      True

  Python

      >>> from sage.all import *
      >>> g = graphs.RandomGNP(Integer(15), RealNumber('.3'))
      >>> (edge_connectivity(g, implementation='boost')                             # needs sage.numerical.mip
      ...    == edge_connectivity(g, implementation='sage'))
      True

  Boost interface also works with directed graphs:

  Sage

      sage: edge_connectivity(digraphs.Circuit(10), implementation='boost',
      ....:                   vertices=True)
      [1, [(0, 1)], [{0}, {1, 2, 3, 4, 5, 6, 7, 8, 9}]]

  [Python]

      >>> from sage.all import *
      >>> edge_connectivity(digraphs.Circuit(Integer(10)), implementation='boost',
      ...                   vertices=True)
      [1, [(0, 1)], [{0}, {1, 2, 3, 4, 5, 6, 7, 8, 9}]]

  However, the Boost algorithm is not reliable if the input is directed (see Issue #18753):

  Sage

      sage: g = digraphs.Path(3)
      sage: edge_connectivity(g)
      0.0
      sage: edge_connectivity(g, implementation='boost')
      1
      sage: g.add_edge(1, 0)
      sage: edge_connectivity(g)
      0.0
      sage: edge_connectivity(g, implementation='boost')
      0

  Python

      >>> from sage.all import *
      >>> g = digraphs.Path(Integer(3))
      >>> edge_connectivity(g)
      0.0
      >>> edge_connectivity(g, implementation='boost')
      1
      >>> g.add_edge(Integer(1), Integer(0))
      >>> edge_connectivity(g)
      0.0
      >>> edge_connectivity(g, implementation='boost')
      0

sage.graphs.connectivity.is_biconnected(G, forbidden_vertices=None)

  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.

  INPUT:

  - `G` – a Sage graph

  - `forbidden_vertices` – list (default: `None`); set of vertices to avoid during the search. This is equivalent to checking whether the graph in which the forbidden vertices have been removed is biconnected.

  See also

  - `is_connected()`

  - `blocks_and_cut_vertices()`

  - `blocks_and_cuts_tree()`

  - Wikipedia article Biconnected_graph

  EXAMPLES:

  Sage

      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

  Python

      >>> from sage.all import *
      >>> G = graphs.PetersenGraph()
      >>> G.is_biconnected()
      True
      >>> G.add_path([Integer(0),'a','b'])
      >>> G.is_biconnected()
      False
      >>> G.add_edge('b', Integer(1))
      >>> G.is_biconnected()
      True

  Check the behavior of parameter `forbidden_vertices`:

  Sage

      sage: G = graphs.CycleGraph(5)
      sage: G.is_biconnected()
      True
      sage: G.is_biconnected(forbidden_vertices=[0])
      False

  [Python]

      >>> from sage.all import *
      >>> G = graphs.CycleGraph(Integer(5))
      >>> G.is_biconnected()
      True
      >>> G.is_biconnected(forbidden_vertices=[Integer(0)])
      False

sage.graphs.connectivity.is_connected(G, forbidden_vertices=None)

  Check whether the (di)graph is connected.

  Note that in a graph, path connected is equivalent to connected.

  INPUT:

  - `G` – the input graph

  - `forbidden_vertices` – list (default: `None`); set of vertices to avoid during the search

  See also

  - `is_biconnected()`

  EXAMPLES:

  Sage

      sage: from sage.graphs.connectivity import is_connected
      sage: G = Graph({0: [1, 2], 1: [2], 3: [4, 5], 4: [5]})
      sage: is_connected(G)
      False
      sage: G.is_connected()
      False
      sage: G.add_edge(0,3)
      sage: is_connected(G)
      True
      sage: is_connected(G, forbidden_vertices=[3])
      False
      sage: is_connected(G, forbidden_vertices=[1])
      True
      sage: D = DiGraph({0: [1, 2], 1: [2], 3: [4, 5], 4: [5]})
      sage: is_connected(D)
      False
      sage: D.add_edge(0, 3)
      sage: is_connected(D)
      True
      sage: D = DiGraph({1: [0], 2: [0]})
      sage: is_connected(D)
      True

  Python

      >>> from sage.all import *
      >>> from sage.graphs.connectivity import is_connected
      >>> G = Graph({Integer(0): [Integer(1), Integer(2)], Integer(1): [Integer(2)], Integer(3): [Integer(4), Integer(5)], Integer(4): [Integer(5)]})
      >>> is_connected(G)
      False
      >>> G.is_connected()
      False
      >>> G.add_edge(Integer(0),Integer(3))
      >>> is_connected(G)
      True
      >>> is_connected(G, forbidden_vertices=[Integer(3)])
      False
      >>> is_connected(G, forbidden_vertices=[Integer(1)])
      True
      >>> D = DiGraph({Integer(0): [Integer(1), Integer(2)], Integer(1): [Integer(2)], Integer(3): [Integer(4), Integer(5)], Integer(4): [Integer(5)]})
      >>> is_connected(D)
      False
      >>> D.add_edge(Integer(0), Integer(3))
      >>> is_connected(D)
      True
      >>> D = DiGraph({Integer(1): [Integer(0)], Integer(2): [Integer(0)]})
      >>> is_connected(D)
      True

sage.graphs.connectivity.is_cut_edge(G, u, v=None, label=None)

  Check whether the edge `(u, v)` is a cut-edge or a bridge of graph `G`.

  A cut edge (or bridge) is an edge that when removed increases the number of connected components. This function works with simple graphs as well as graphs with loops and multiedges. In a digraph, a cut edge is an edge that when removed increases the number of (weakly) connected components.

  INPUT: The following forms are accepted

  - is_cut_edge(G, 1, 2 )

  - is_cut_edge(G, (1, 2) )

  - is_cut_edge(G, 1, 2, ‘label’ )

  - is_cut_edge(G, (1, 2, ‘label’) )

  OUTPUT:

  - Returns `True` if (u,v) is a cut edge, False otherwise

  EXAMPLES:

  Sage

      sage: from sage.graphs.connectivity import is_cut_edge
      sage: G = graphs.CompleteGraph(4)
      sage: is_cut_edge(G,0,2)
      False
      sage: G.is_cut_edge(0,2)
      False

      sage: G = graphs.CompleteGraph(4)
      sage: G.add_edge((0,5,'silly'))
      sage: is_cut_edge(G,(0,5,'silly'))
      True

      sage: G = Graph([[0,1],[0,2],[3,4],[4,5],[3,5]])
      sage: is_cut_edge(G,(0,1))
      True

      sage: G = Graph([[0,1],[0,2],[1,1]], loops = True)
      sage: is_cut_edge(G,(1,1))
      False

      sage: G = digraphs.Circuit(5)
      sage: is_cut_edge(G,(0,1))
      False

      sage: G = graphs.CompleteGraph(6)
      sage: is_cut_edge(G,(0,7))
      Traceback (most recent call last):
      ...
      ValueError: edge not in graph

  Python

      >>> from sage.all import *
      >>> from sage.graphs.connectivity import is_cut_edge
      >>> G = graphs.CompleteGraph(Integer(4))
      >>> is_cut_edge(G,Integer(0),Integer(2))
      False
      >>> G.is_cut_edge(Integer(0),Integer(2))
      False

      >>> G = graphs.CompleteGraph(Integer(4))
      >>> G.add_edge((Integer(0),Integer(5),'silly'))
      >>> is_cut_edge(G,(Integer(0),Integer(5),'silly'))
      True

      >>> G = Graph([[Integer(0),Integer(1)],[Integer(0),Integer(2)],[Integer(3),Integer(4)],[Integer(4),Integer(5)],[Integer(3),Integer(5)]])
      >>> is_cut_edge(G,(Integer(0),Integer(1)))
      True

      >>> G = Graph([[Integer(0),Integer(1)],[Integer(0),Integer(2)],[Integer(1),Integer(1)]], loops = True)
      >>> is_cut_edge(G,(Integer(1),Integer(1)))
      False

      >>> G = digraphs.Circuit(Integer(5))
      >>> is_cut_edge(G,(Integer(0),Integer(1)))
      False

      >>> G = graphs.CompleteGraph(Integer(6))
      >>> is_cut_edge(G,(Integer(0),Integer(7)))
      Traceback (most recent call last):
      ...
      ValueError: edge not in graph

sage.graphs.connectivity.is_cut_vertex(G, u, weak=False)

  Check whether the input vertex is a cut-vertex.

  A vertex is a cut-vertex if its removal from the (di)graph increases the number of (strongly) connected components. Isolated vertices or leaves are not cut-vertices. This function works with simple graphs as well as graphs with loops and multiple edges.

  INPUT:

  - `G` – a Sage (Di)Graph

  - `u` – a vertex

  - `weak` – boolean (default: `False`); whether the connectivity of directed graphs is to be taken in the weak sense, that is ignoring edges orientations

  OUTPUT:

  Return `True` if `u` is a cut-vertex, and `False` otherwise.

  EXAMPLES:

  Giving a LollipopGraph(4,2), that is a complete graph with 4 vertices with a pending edge:

  Sage

      sage: from sage.graphs.connectivity import is_cut_vertex
      sage: G = graphs.LollipopGraph(4, 2)
      sage: is_cut_vertex(G, 0)
      False
      sage: is_cut_vertex(G, 3)
      True
      sage: G.is_cut_vertex(3)
      True

  Python

      >>> from sage.all import *
      >>> from sage.graphs.connectivity import is_cut_vertex
      >>> G = graphs.LollipopGraph(Integer(4), Integer(2))
      >>> is_cut_vertex(G, Integer(0))
      False
      >>> is_cut_vertex(G, Integer(3))
      True
      >>> G.is_cut_vertex(Integer(3))
      True

  Comparing the weak and strong connectivity of a digraph:

  Sage

      sage: D = digraphs.Circuit(6)
      sage: D.is_strongly_connected()
      True
      sage: is_cut_vertex(D, 2)
      True
      sage: is_cut_vertex(D, 2, weak=True)
      False

  [Python]

      >>> from sage.all import *
      >>> D = digraphs.Circuit(Integer(6))
      >>> D.is_strongly_connected()
      True
      >>> is_cut_vertex(D, Integer(2))
      True
      >>> is_cut_vertex(D, Integer(2), weak=True)
      False

  Giving a vertex that is not in the graph:

  Sage

      sage: G = graphs.CompleteGraph(4)
      sage: is_cut_vertex(G, 7)
      Traceback (most recent call last):
      ...
      ValueError: vertex (7) is not a vertex of the graph

  Python

      >>> from sage.all import *
      >>> G = graphs.CompleteGraph(Integer(4))
      >>> is_cut_vertex(G, Integer(7))
      Traceback (most recent call last):
      ...
      ValueError: vertex (7) is not a vertex of the graph

sage.graphs.connectivity.is_edge_cut(G, edges)

  Check whether `edges` form an edge cut.

  A set of edges is an edge cut of a graph if its removal increases the number of connected components. In a digraph, we consider the number of (weakly) connected components.

  This method is not working for (di)graphs with multiple edges. Furthermore, edge labels are ignored.

  INPUT:

  - `G` – a (di)graph

  - `edges` – a set of edges

  EXAMPLES:

  A cycle graph of order 4:

  Sage

      sage: from sage.graphs.connectivity import is_edge_cut
      sage: G = graphs.CycleGraph(4)
      sage: is_edge_cut(G, [(1, 2)])
      False
      sage: is_edge_cut(G, [(1, 2), (2, 3)])
      True
      sage: is_edge_cut(G, [(1, 2), (3, 0)])
      True

  Python

      >>> from sage.all import *
      >>> from sage.graphs.connectivity import is_edge_cut
      >>> G = graphs.CycleGraph(Integer(4))
      >>> is_edge_cut(G, [(Integer(1), Integer(2))])
      False
      >>> is_edge_cut(G, [(Integer(1), Integer(2)), (Integer(2), Integer(3))])
      True
      >>> is_edge_cut(G, [(Integer(1), Integer(2)), (Integer(3), Integer(0))])
      True

  A pending edge is a cut-edge:

  Sage

      sage: G.add_edge((0, 5, 'silly'))
      sage: is_edge_cut(G, [(0, 5, 'silly')])
      True

  [Python]

      >>> from sage.all import *
      >>> G.add_edge((Integer(0), Integer(5), 'silly'))
      >>> is_edge_cut(G, [(Integer(0), Integer(5), 'silly')])
      True

  Edge labels are ignored, even if specified:

  Sage

      sage: G.add_edge((2, 5, 'xyz'))
      sage: is_edge_cut(G, [(0, 5), (2, 5)])
      True
      sage: is_edge_cut(G, [(0, 5), (2, 5, 'xyz')])
      True
      sage: is_edge_cut(G, [(0, 5, 'silly'), (2, 5)])
      True
      sage: is_edge_cut(G, [(0, 5, 'aa'), (2, 5, 'bb')])
      True

  Python

      >>> from sage.all import *
      >>> G.add_edge((Integer(2), Integer(5), 'xyz'))
      >>> is_edge_cut(G, [(Integer(0), Integer(5)), (Integer(2), Integer(5))])
      True
      >>> is_edge_cut(G, [(Integer(0), Integer(5)), (Integer(2), Integer(5), 'xyz')])
      True
      >>> is_edge_cut(G, [(Integer(0), Integer(5), 'silly'), (Integer(2), Integer(5))])
      True
      >>> is_edge_cut(G, [(Integer(0), Integer(5), 'aa'), (Integer(2), Integer(5), 'bb')])
      True

  The graph can have loops:

  Sage

      sage: G.allow_loops(True)
      sage: G.add_edge(0, 0)
      sage: is_edge_cut(G, [(0, 5), (2, 5)])
      True
      sage: is_edge_cut(G, [(0, 0), (0, 5), (2, 5)])
      True

  [Python]

      >>> from sage.all import *
      >>> G.allow_loops(True)
      >>> G.add_edge(Integer(0), Integer(0))
      >>> is_edge_cut(G, [(Integer(0), Integer(5)), (Integer(2), Integer(5))])
      True
      >>> is_edge_cut(G, [(Integer(0), Integer(0)), (Integer(0), Integer(5)), (Integer(2), Integer(5))])
      True

  Multiple edges are not allowed:

  Sage

      sage: G.allow_multiple_edges(True)
      sage: is_edge_cut(G, [(0, 5), (2, 5)])
      Traceback (most recent call last):
      ...
      ValueError: This method is not known to work on graphs with
       multiedges. Perhaps this method can be updated to handle them, but in
       the meantime if you want to use it please disallow multiedges using
       allow_multiple_edges().

  Python

      >>> from sage.all import *
      >>> G.allow_multiple_edges(True)
      >>> is_edge_cut(G, [(Integer(0), Integer(5)), (Integer(2), Integer(5))])
      Traceback (most recent call last):
      ...
      ValueError: This method is not known to work on graphs with
       multiedges. Perhaps this method can be updated to handle them, but in
       the meantime if you want to use it please disallow multiedges using
       allow_multiple_edges().

  An error is raised if an element of `edges` is not an edge of \(G\):

  Sage

      sage: G = graphs.CycleGraph(4)
      sage: is_edge_cut(G, [(0, 2)])
      Traceback (most recent call last):
      ...
      ValueError: edge (0, 2) is not an edge of the graph

  [Python]

      >>> from sage.all import *
      >>> G = graphs.CycleGraph(Integer(4))
      >>> is_edge_cut(G, [(Integer(0), Integer(2))])
      Traceback (most recent call last):
      ...
      ValueError: edge (0, 2) is not an edge of the graph

  For digraphs, this method considers the number of (weakly) connected components:

  Sage

      sage: G = digraphs.Circuit(4)
      sage: is_edge_cut(G, [(0, 1)])
      False
      sage: G = digraphs.Circuit(4)
      sage: is_edge_cut(G, [(0, 1), (1, 2)])
      True

  Python

      >>> from sage.all import *
      >>> G = digraphs.Circuit(Integer(4))
      >>> is_edge_cut(G, [(Integer(0), Integer(1))])
      False
      >>> G = digraphs.Circuit(Integer(4))
      >>> is_edge_cut(G, [(Integer(0), Integer(1)), (Integer(1), Integer(2))])
      True

  For disconnected (di)graphs, the method checks if the number of (weakly) connected components increases:

  Sage

      sage: G = graphs.CycleGraph(4) * 2
      sage: is_edge_cut(G, [(1, 2), (2, 3)])
      True
      sage: G = digraphs.Circuit(4) * 2
      sage: is_edge_cut(G, [(0, 1), (1, 2)])
      True

  [Python]

      >>> from sage.all import *
      >>> G = graphs.CycleGraph(Integer(4)) * Integer(2)
      >>> is_edge_cut(G, [(Integer(1), Integer(2)), (Integer(2), Integer(3))])
      True
      >>> G = digraphs.Circuit(Integer(4)) * Integer(2)
      >>> is_edge_cut(G, [(Integer(0), Integer(1)), (Integer(1), Integer(2))])
      True

sage.graphs.connectivity.is_strongly_connected(G)

  Check whether the current `DiGraph` is strongly connected.

  EXAMPLES:

  The circuit is obviously strongly connected:

  Sage

      sage: from sage.graphs.connectivity import is_strongly_connected
      sage: g = digraphs.Circuit(5)
      sage: is_strongly_connected(g)
      True
      sage: g.is_strongly_connected()
      True

  Python

      >>> from sage.all import *
      >>> from sage.graphs.connectivity import is_strongly_connected
      >>> g = digraphs.Circuit(Integer(5))
      >>> is_strongly_connected(g)
      True
      >>> g.is_strongly_connected()
      True

  But a transitive triangle is not:

  Sage

      sage: g = DiGraph({0: [1, 2], 1: [2]})
      sage: is_strongly_connected(g)
      False

  [Python]

      >>> from sage.all import *
      >>> g = DiGraph({Integer(0): [Integer(1), Integer(2)], Integer(1): [Integer(2)]})
      >>> is_strongly_connected(g)
      False

sage.graphs.connectivity.is_triconnected(G)

  Check whether the graph is triconnected.

  A triconnected graph is a connected graph on 3 or more vertices that is not broken into disconnected pieces by deleting any pair of vertices.

  EXAMPLES:

  The Petersen graph is triconnected:

  Sage

      sage: G = graphs.PetersenGraph()
      sage: G.is_triconnected()
      True

  Python

      >>> from sage.all import *
      >>> G = graphs.PetersenGraph()
      >>> G.is_triconnected()
      True

  But a 2D grid is not:

  Sage

      sage: G = graphs.Grid2dGraph(3, 3)
      sage: G.is_triconnected()
      False

  [Python]

      >>> from sage.all import *
      >>> G = graphs.Grid2dGraph(Integer(3), Integer(3))
      >>> G.is_triconnected()
      False

  By convention, a cycle of order 3 is triconnected:

  Sage

      sage: G = graphs.CycleGraph(3)
      sage: G.is_triconnected()
      True

  Python

      >>> from sage.all import *
      >>> G = graphs.CycleGraph(Integer(3))
      >>> G.is_triconnected()
      True

  But cycles of order 4 and more are not:

  Sage

      sage: [graphs.CycleGraph(i).is_triconnected() for i in range(4, 8)]
      [False, False, False, False]

  [Python]

      >>> from sage.all import *
      >>> [graphs.CycleGraph(i).is_triconnected() for i in range(Integer(4), Integer(8))]
      [False, False, False, False]

  Comparing different methods on random graphs that are not always triconnected:

  Sage

      sage: G = graphs.RandomBarabasiAlbert(50, 3)                                    # needs networkx
      sage: G.is_triconnected() == G.vertex_connectivity(k=3)                         # needs networkx
      True

  Python

      >>> from sage.all import *
      >>> G = graphs.RandomBarabasiAlbert(Integer(50), Integer(3))                                    # needs networkx
      >>> G.is_triconnected() == G.vertex_connectivity(k=Integer(3))                         # needs networkx
      True

  See also

  - `is_connected()`

  - `is_biconnected()`

  - `spqr_tree()`

  - Wikipedia article SPQR_tree

sage.graphs.connectivity.is_vertex_cut(G, cut, weak=False)

  Check whether the input vertices form a vertex cut.

  A set of vertices is a vertex cut if its removal from the (di)graph increases the number of (strongly) connected components. This function works with simple graphs as well as graphs with loops and multiple edges.

  INPUT:

  - `G` – a Sage (Di)Graph

  - `cut` – a set of vertices

  - `weak` – boolean (default: `False`); whether the connectivity of directed graphs is to be taken in the weak sense, that is ignoring edges orientations

  EXAMPLES:

  Giving a cycle graph of order 4:

  Sage

      sage: from sage.graphs.connectivity import is_vertex_cut
      sage: G = graphs.CycleGraph(4)
      sage: is_vertex_cut(G, [0, 1])
      False
      sage: is_vertex_cut(G, [0, 2])
      True

  Python

      >>> from sage.all import *
      >>> from sage.graphs.connectivity import is_vertex_cut
      >>> G = graphs.CycleGraph(Integer(4))
      >>> is_vertex_cut(G, [Integer(0), Integer(1)])
      False
      >>> is_vertex_cut(G, [Integer(0), Integer(2)])
      True

  Giving a disconnected graph:

  Sage

      sage: from sage.graphs.connectivity import is_vertex_cut
      sage: G = graphs.CycleGraph(4) * 2
      sage: G.connected_components(sort=True)
      [[0, 1, 2, 3], [4, 5, 6, 7]]
      sage: is_vertex_cut(G, [0, 2])
      True
      sage: is_vertex_cut(G, [4, 6])
      True
      sage: is_vertex_cut(G, [0, 6])
      False
      sage: is_vertex_cut(G, [0, 4, 6])
      True

  [Python]

      >>> from sage.all import *
      >>> from sage.graphs.connectivity import is_vertex_cut
      >>> G = graphs.CycleGraph(Integer(4)) * Integer(2)
      >>> G.connected_components(sort=True)
      [[0, 1, 2, 3], [4, 5, 6, 7]]
      >>> is_vertex_cut(G, [Integer(0), Integer(2)])
      True
      >>> is_vertex_cut(G, [Integer(4), Integer(6)])
      True
      >>> is_vertex_cut(G, [Integer(0), Integer(6)])
      False
      >>> is_vertex_cut(G, [Integer(0), Integer(4), Integer(6)])
      True

  Comparing the weak and strong connectivity of a digraph:

  Sage

      sage: D = digraphs.Circuit(6)
      sage: D.is_strongly_connected()
      True
      sage: is_vertex_cut(D, [2])
      True
      sage: is_vertex_cut(D, [2], weak=True)
      False

  Python

      >>> from sage.all import *
      >>> D = digraphs.Circuit(Integer(6))
      >>> D.is_strongly_connected()
      True
      >>> is_vertex_cut(D, [Integer(2)])
      True
      >>> is_vertex_cut(D, [Integer(2)], weak=True)
      False

  Giving a vertex that is not in the graph:

  Sage

      sage: G = graphs.CompleteGraph(4)
      sage: is_vertex_cut(G, [7])
      Traceback (most recent call last):
      ...
      ValueError: vertex (7) is not a vertex of the graph

  [Python]

      >>> from sage.all import *
      >>> G = graphs.CompleteGraph(Integer(4))
      >>> is_vertex_cut(G, [Integer(7)])
      Traceback (most recent call last):
      ...
      ValueError: vertex (7) is not a vertex of the graph

sage.graphs.connectivity.minimal_separators(G, forbidden_vertices=None)

  Return an iterator over the minimal separators of `G`.

  A separator in a graph is a set of vertices whose removal increases the number of connected components. In other words, a separator is a vertex cut. This method implements the algorithm proposed in [BBC2000]. It computes the set \(S\) of minimal separators of a graph in \(O(n^3)\) time per separator, and so overall in \(O(n^3 |S|)\) time.

  Warning

  Note that all separators are recorded during the execution of the algorithm and so the memory consumption of this method might be huge.

  INPUT:

  - `G` – an undirected graph

  - `forbidden_vertices` – list (default: `None`); set of vertices to avoid during the search

  EXAMPLES:

  Sage

      sage: P = graphs.PathGraph(5)
      sage: sorted(sorted(sep) for sep in P.minimal_separators())
      [[1], [2], [3]]
      sage: C = graphs.CycleGraph(6)
      sage: sorted(sorted(sep) for sep in C.minimal_separators())
      [[0, 2], [0, 3], [0, 4], [1, 3], [1, 4], [1, 5], [2, 4], [2, 5], [3, 5]]
      sage: sorted(sorted(sep) for sep in C.minimal_separators(forbidden_vertices=[0]))
      [[2], [3], [4]]
      sage: sorted(sorted(sep) for sep in (P + C).minimal_separators())
      [[1], [2], [3], [5, 7], [5, 8], [5, 9], [6, 8],
       [6, 9], [6, 10], [7, 9], [7, 10], [8, 10]]
      sage: sorted(sorted(sep) for sep in (P + C).minimal_separators(forbidden_vertices=[10]))
      [[1], [2], [3], [6], [7], [8]]

      sage: G = graphs.RandomGNP(10, .3)
      sage: all(G.is_vertex_cut(sep) for sep in G.minimal_separators())
      True

  Python

      >>> from sage.all import *
      >>> P = graphs.PathGraph(Integer(5))
      >>> sorted(sorted(sep) for sep in P.minimal_separators())
      [[1], [2], [3]]
      >>> C = graphs.CycleGraph(Integer(6))
      >>> sorted(sorted(sep) for sep in C.minimal_separators())
      [[0, 2], [0, 3], [0, 4], [1, 3], [1, 4], [1, 5], [2, 4], [2, 5], [3, 5]]
      >>> sorted(sorted(sep) for sep in C.minimal_separators(forbidden_vertices=[Integer(0)]))
      [[2], [3], [4]]
      >>> sorted(sorted(sep) for sep in (P + C).minimal_separators())
      [[1], [2], [3], [5, 7], [5, 8], [5, 9], [6, 8],
       [6, 9], [6, 10], [7, 9], [7, 10], [8, 10]]
      >>> sorted(sorted(sep) for sep in (P + C).minimal_separators(forbidden_vertices=[Integer(10)]))
      [[1], [2], [3], [6], [7], [8]]

      >>> G = graphs.RandomGNP(Integer(10), RealNumber('.3'))
      >>> all(G.is_vertex_cut(sep) for sep in G.minimal_separators())
      True

  Example of [BPS2010]:

  Sage

      sage: G = Graph({'a': ['b', 'k'], 'b': ['c'], 'c': ['d', 'j', 'k'],
      ....:            'd': ['e', 'f', 'j', 'k'], 'e': ['g'],
      ....:            'f': ['g', 'j', 'k'], 'g': ['j', 'k'], 'h': ['i', 'j'],
      ....:            'i': ['k'], 'j': ['k']})
      sage: sorted(sorted(sep) for sep in G.minimal_separators())
      [['a', 'c'], ['b', 'k'], ['c', 'k'], ['d', 'j', 'k'],
       ['h', 'k'], ['i', 'j'], ['j', 'k']]

  [Python]

      >>> from sage.all import *
      >>> G = Graph({'a': ['b', 'k'], 'b': ['c'], 'c': ['d', 'j', 'k'],
      ...            'd': ['e', 'f', 'j', 'k'], 'e': ['g'],
      ...            'f': ['g', 'j', 'k'], 'g': ['j', 'k'], 'h': ['i', 'j'],
      ...            'i': ['k'], 'j': ['k']})
      >>> sorted(sorted(sep) for sep in G.minimal_separators())
      [['a', 'c'], ['b', 'k'], ['c', 'k'], ['d', 'j', 'k'],
       ['h', 'k'], ['i', 'j'], ['j', 'k']]

sage.graphs.connectivity.number_of_biconnected_components(G, forbidden_vertices=None)

  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.

  INPUT:

  - `G` – the input graph

  - `forbidden_vertices` – list (default: `None`); set of vertices to avoid during the search. This is equivalent to getting the number of biconnected components of the graph after the removal of the forbidden vertices.

  See also

  - `blocks_and_cut_vertices()`

  - `is_biconnected()`

  EXAMPLES:

  The disjoint union of cycles has as many biconnected components as the number of cycles:

  Sage

      sage: G = graphs.CycleGraph(5)
      sage: G.number_of_biconnected_components()
      1
      sage: (3 * G).number_of_biconnected_components()
      3

  Python

      >>> from sage.all import *
      >>> G = graphs.CycleGraph(Integer(5))
      >>> G.number_of_biconnected_components()
      1
      >>> (Integer(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

      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

  [Python]

      >>> from sage.all import *
      >>> number_of_blocks = randint(Integer(4), Integer(10))
      >>> G = graphs.RandomBlockGraph(number_of_blocks, Integer(5))
      >>> 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

      sage: T = graphs.RandomTree(randint(0, 10))
      sage: T.number_of_biconnected_components() == T.size()
      True

  Python

      >>> from sage.all import *
      >>> T = graphs.RandomTree(randint(Integer(0), Integer(10)))
      >>> T.number_of_biconnected_components() == T.size()
      True

  An isolated vertex is a block but not a biconnected component:

  Sage

      sage: G = Graph(3)
      sage: len(G.blocks_and_cut_vertices()[0])
      3
      sage: G.number_of_biconnected_components()
      0

  [Python]

      >>> from sage.all import *
      >>> G = Graph(Integer(3))
      >>> len(G.blocks_and_cut_vertices()[Integer(0)])
      3
      >>> G.number_of_biconnected_components()
      0

  Check the behavior of parameter `forbidden_vertices`:

  Sage

      sage: G = graphs.CycleGraph(5)
      sage: G.number_of_biconnected_components()
      1
      sage: G.number_of_biconnected_components(forbidden_vertices=[0])
      3

  Python

      >>> from sage.all import *
      >>> G = graphs.CycleGraph(Integer(5))
      >>> G.number_of_biconnected_components()
      1
      >>> G.number_of_biconnected_components(forbidden_vertices=[Integer(0)])
      3

sage.graphs.connectivity.number_of_connected_components(G, forbidden_vertices=None)

  Return the number of connected components.

  INPUT:

  - `G` – the input graph

  - `forbidden_vertices` – list (default: `None`); set of vertices to avoid during the search

  EXAMPLES:

  Sage

      sage: from sage.graphs.connectivity import number_of_connected_components
      sage: G = Graph({0: [1, 3], 1: [2], 2: [3], 4: [5, 6], 5: [6]})
      sage: number_of_connected_components(G)
      2
      sage: G.number_of_connected_components()
      2
      sage: D = DiGraph({0: [1, 3], 1: [2], 2: [3], 4: [5, 6], 5: [6]})
      sage: number_of_connected_components(D)
      2
      sage: number_of_connected_components(D, forbidden_vertices=[1, 3])
      3

  Python

      >>> from sage.all import *
      >>> from sage.graphs.connectivity import number_of_connected_components
      >>> G = Graph({Integer(0): [Integer(1), Integer(3)], Integer(1): [Integer(2)], Integer(2): [Integer(3)], Integer(4): [Integer(5), Integer(6)], Integer(5): [Integer(6)]})
      >>> number_of_connected_components(G)
      2
      >>> G.number_of_connected_components()
      2
      >>> D = DiGraph({Integer(0): [Integer(1), Integer(3)], Integer(1): [Integer(2)], Integer(2): [Integer(3)], Integer(4): [Integer(5), Integer(6)], Integer(5): [Integer(6)]})
      >>> number_of_connected_components(D)
      2
      >>> number_of_connected_components(D, forbidden_vertices=[Integer(1), Integer(3)])
      3

sage.graphs.connectivity.spqr_tree(G, algorithm='Hopcroft_Tarjan', solver=None, verbose=0, integrality_tolerance=0.001)

  Return an SPQR-tree representing the triconnected components of the graph.

  An SPQR-tree is a tree data structure used to represent the triconnected components of a biconnected (multi)graph and the 2-vertex cuts separating them. A node of a SPQR-tree, and the graph associated with it, can be one of the following four types:

  - `'S'` – the associated graph is a cycle with at least three vertices `'S'` stands for `series`

  - `'P'` – the associated graph is a dipole graph, a multigraph with two vertices and three or more edges. `'P'` stands for `parallel`

  - `'Q'` – the associated graph has a single real edge. This trivial case is necessary to handle the graph that has only one edge

  - `'R'` – the associated graph is a 3-connected graph that is not a cycle or dipole. `'R'` stands for `rigid`

  This method decomposes a biconnected graph into cycles, cocycles, and 3-connected blocks summed over cocycles, and arranges them as a SPQR-tree. More precisely, it splits the graph at each of its 2-vertex cuts, giving a unique decomposition into 3-connected blocks, cycles and cocycles. The cocycles are dipole graphs with one edge per real edge between the included vertices and one additional (virtual) edge per connected component resulting from deletion of the vertices in the cut. See the Wikipedia article SPQR_tree.

  INPUT:

  - `G` – the input graph

  - `algorithm` – string (default: `'Hopcroft_Tarjan'`); the algorithm to use among:

    - `'Hopcroft_Tarjan'` – default; use the algorithm proposed by Hopcroft and Tarjan in [Hopcroft1973] and later corrected by Gutwenger and Mutzel in [Gut2001]. See `TriconnectivitySPQR`.

    - `'cleave'` – using method `cleave()`

  - `solver` – string (default: `None`); specifies a Mixed Integer Linear Programming (MILP) solver to be used. If set to `None`, the default one is used. For more information on MILP solvers and which default solver is used, see the method `solve` of the class `MixedIntegerLinearProgram`.

  - `verbose` – integer (default: 0); sets the level of verbosity. Set to 0 by default, which means quiet.

  - `integrality_tolerance` – float; parameter for use with MILP solvers over an inexact base ring; see `MixedIntegerLinearProgram.get_values()`.

  OUTPUT: `SPQR-tree` a tree whose vertices are labeled with the block’s type and the subgraph of three-blocks in the decomposition.

  EXAMPLES:

  Sage

      sage: from sage.graphs.connectivity import spqr_tree
      sage: G = Graph(2)
      sage: for i in range(3):
      ....:     G.add_clique([0, 1, G.add_vertex(), G.add_vertex()])
      sage: Tree = spqr_tree(G)
      sage: Tree.order()
      4
      sage: K4 = graphs.CompleteGraph(4)
      sage: all(u[1].is_isomorphic(K4) for u in Tree if u[0] == 'R')
      True
      sage: from sage.graphs.connectivity import spqr_tree_to_graph
      sage: G.is_isomorphic(spqr_tree_to_graph(Tree))
      True

      sage: G = Graph(2)
      sage: for i in range(3):
      ....:     G.add_path([0, G.add_vertex(), G.add_vertex(), 1])
      sage: Tree = spqr_tree(G)
      sage: Tree.order()
      4
      sage: C4 = graphs.CycleGraph(4)
      sage: all(u[1].is_isomorphic(C4) for u in Tree if u[0] == 'S')
      True
      sage: G.is_isomorphic(spqr_tree_to_graph(Tree))
      True

      sage: G.allow_multiple_edges(True)
      sage: G.add_edges(G.edge_iterator())
      sage: Tree = spqr_tree(G)
      sage: Tree.order()
      13
      sage: all(u[1].is_isomorphic(C4) for u in Tree if u[0] == 'S')
      True
      sage: G.is_isomorphic(spqr_tree_to_graph(Tree))
      True

      sage: G = graphs.CycleGraph(6)
      sage: Tree = spqr_tree(G)
      sage: Tree.order()
      1
      sage: G.is_isomorphic(spqr_tree_to_graph(Tree))
      True
      sage: G.add_edge(0, 3)
      sage: Tree = spqr_tree(G)
      sage: Tree.order()
      3
      sage: G.is_isomorphic(spqr_tree_to_graph(Tree))
      True

      sage: G = Graph('LlCG{O@?GBoMw?')
      sage: T = spqr_tree(G, algorithm='Hopcroft_Tarjan')
      sage: G.is_isomorphic(spqr_tree_to_graph(T))
      True
      sage: T2 = spqr_tree(G, algorithm='cleave')                                     # needs sage.numerical.mip
      sage: G.is_isomorphic(spqr_tree_to_graph(T2))                                   # needs sage.numerical.mip
      True

      sage: G = Graph([(0, 1)], multiedges=True)
      sage: T = spqr_tree(G, algorithm='cleave')                                      # needs sage.numerical.mip
      sage: T.vertices(sort=True)                                                     # needs sage.numerical.mip
      [('Q', Multi-graph on 2 vertices)]
      sage: G.is_isomorphic(spqr_tree_to_graph(T))                                    # needs sage.numerical.mip
      True
      sage: T = spqr_tree(G, algorithm='Hopcroft_Tarjan')
      sage: T.vertices(sort=True)
      [('Q', Multi-graph on 2 vertices)]
      sage: G.add_edge(0, 1)
      sage: spqr_tree(G, algorithm='cleave').vertices(sort=True)                      # needs sage.numerical.mip
      [('P', Multi-graph on 2 vertices)]

      sage: from collections import Counter
      sage: G = graphs.PetersenGraph()
      sage: T = G.spqr_tree(algorithm='Hopcroft_Tarjan')
      sage: Counter(u[0] for u in T)
      Counter({'R': 1})
      sage: T = G.spqr_tree(algorithm='cleave')                                       # needs sage.numerical.mip
      sage: Counter(u[0] for u in T)                                                  # needs sage.numerical.mip
      Counter({'R': 1})
      sage: for u,v in list(G.edges(labels=False, sort=False)):
      ....:     G.add_path([u, G.add_vertex(), G.add_vertex(), v])
      sage: T = G.spqr_tree(algorithm='Hopcroft_Tarjan')
      sage: sorted(Counter(u[0] for u in T).items())
      [('P', 15), ('R', 1), ('S', 15)]
      sage: T = G.spqr_tree(algorithm='cleave')                                       # needs sage.numerical.mip
      sage: sorted(Counter(u[0] for u in T).items())                                  # needs sage.numerical.mip
      [('P', 15), ('R', 1), ('S', 15)]
      sage: for u,v in list(G.edges(labels=False, sort=False)):
      ....:     G.add_path([u, G.add_vertex(), G.add_vertex(), v])
      sage: T = G.spqr_tree(algorithm='Hopcroft_Tarjan')
      sage: sorted(Counter(u[0] for u in T).items())
      [('P', 60), ('R', 1), ('S', 75)]
      sage: T = G.spqr_tree(algorithm='cleave')       # long time                     # needs sage.numerical.mip
      sage: sorted(Counter(u[0] for u in T).items())  # long time                     # needs sage.numerical.mip
      [('P', 60), ('R', 1), ('S', 75)]

  Python

      >>> from sage.all import *
      >>> from sage.graphs.connectivity import spqr_tree
      >>> G = Graph(Integer(2))
      >>> for i in range(Integer(3)):
      ...     G.add_clique([Integer(0), Integer(1), G.add_vertex(), G.add_vertex()])
      >>> Tree = spqr_tree(G)
      >>> Tree.order()
      4
      >>> K4 = graphs.CompleteGraph(Integer(4))
      >>> all(u[Integer(1)].is_isomorphic(K4) for u in Tree if u[Integer(0)] == 'R')
      True
      >>> from sage.graphs.connectivity import spqr_tree_to_graph
      >>> G.is_isomorphic(spqr_tree_to_graph(Tree))
      True

      >>> G = Graph(Integer(2))
      >>> for i in range(Integer(3)):
      ...     G.add_path([Integer(0), G.add_vertex(), G.add_vertex(), Integer(1)])
      >>> Tree = spqr_tree(G)
      >>> Tree.order()
      4
      >>> C4 = graphs.CycleGraph(Integer(4))
      >>> all(u[Integer(1)].is_isomorphic(C4) for u in Tree if u[Integer(0)] == 'S')
      True
      >>> G.is_isomorphic(spqr_tree_to_graph(Tree))
      True

      >>> G.allow_multiple_edges(True)
      >>> G.add_edges(G.edge_iterator())
      >>> Tree = spqr_tree(G)
      >>> Tree.order()
      13
      >>> all(u[Integer(1)].is_isomorphic(C4) for u in Tree if u[Integer(0)] == 'S')
      True
      >>> G.is_isomorphic(spqr_tree_to_graph(Tree))
      True

      >>> G = graphs.CycleGraph(Integer(6))
      >>> Tree = spqr_tree(G)
      >>> Tree.order()
      1
      >>> G.is_isomorphic(spqr_tree_to_graph(Tree))
      True
      >>> G.add_edge(Integer(0), Integer(3))
      >>> Tree = spqr_tree(G)
      >>> Tree.order()
      3
      >>> G.is_isomorphic(spqr_tree_to_graph(Tree))
      True

      >>> G = Graph('LlCG{O@?GBoMw?')
      >>> T = spqr_tree(G, algorithm='Hopcroft_Tarjan')
      >>> G.is_isomorphic(spqr_tree_to_graph(T))
      True
      >>> T2 = spqr_tree(G, algorithm='cleave')                                     # needs sage.numerical.mip
      >>> G.is_isomorphic(spqr_tree_to_graph(T2))                                   # needs sage.numerical.mip
      True

      >>> G = Graph([(Integer(0), Integer(1))], multiedges=True)
      >>> T = spqr_tree(G, algorithm='cleave')                                      # needs sage.numerical.mip
      >>> T.vertices(sort=True)                                                     # needs sage.numerical.mip
      [('Q', Multi-graph on 2 vertices)]
      >>> G.is_isomorphic(spqr_tree_to_graph(T))                                    # needs sage.numerical.mip
      True
      >>> T = spqr_tree(G, algorithm='Hopcroft_Tarjan')
      >>> T.vertices(sort=True)
      [('Q', Multi-graph on 2 vertices)]
      >>> G.add_edge(Integer(0), Integer(1))
      >>> spqr_tree(G, algorithm='cleave').vertices(sort=True)                      # needs sage.numerical.mip
      [('P', Multi-graph on 2 vertices)]

      >>> from collections import Counter
      >>> G = graphs.PetersenGraph()
      >>> T = G.spqr_tree(algorithm='Hopcroft_Tarjan')
      >>> Counter(u[Integer(0)] for u in T)
      Counter({'R': 1})
      >>> T = G.spqr_tree(algorithm='cleave')                                       # needs sage.numerical.mip
      >>> Counter(u[Integer(0)] for u in T)                                                  # needs sage.numerical.mip
      Counter({'R': 1})
      >>> for u,v in list(G.edges(labels=False, sort=False)):
      ...     G.add_path([u, G.add_vertex(), G.add_vertex(), v])
      >>> T = G.spqr_tree(algorithm='Hopcroft_Tarjan')
      >>> sorted(Counter(u[Integer(0)] for u in T).items())
      [('P', 15), ('R', 1), ('S', 15)]
      >>> T = G.spqr_tree(algorithm='cleave')                                       # needs sage.numerical.mip
      >>> sorted(Counter(u[Integer(0)] for u in T).items())                                  # needs sage.numerical.mip
      [('P', 15), ('R', 1), ('S', 15)]
      >>> for u,v in list(G.edges(labels=False, sort=False)):
      ...     G.add_path([u, G.add_vertex(), G.add_vertex(), v])
      >>> T = G.spqr_tree(algorithm='Hopcroft_Tarjan')
      >>> sorted(Counter(u[Integer(0)] for u in T).items())
      [('P', 60), ('R', 1), ('S', 75)]
      >>> T = G.spqr_tree(algorithm='cleave')       # long time                     # needs sage.numerical.mip
      >>> sorted(Counter(u[Integer(0)] for u in T).items())  # long time                     # needs sage.numerical.mip
      [('P', 60), ('R', 1), ('S', 75)]

sage.graphs.connectivity.spqr_tree_to_graph(T)

  Return the graph represented by the SPQR-tree \(T\).

  The main purpose of this method is to test `spqr_tree()`.

  INPUT:

  - `T` – a SPQR tree as returned by `spqr_tree()`

  OUTPUT: a (multi) graph

  EXAMPLES:

  Wikipedia article SPQR_tree reference paper example:

  Sage

      sage: from sage.graphs.connectivity import spqr_tree
      sage: from sage.graphs.connectivity import spqr_tree_to_graph
      sage: G = Graph([(1, 2), (1, 4), (1, 8), (1, 12), (3, 4), (2, 3),
      ....: (2, 13), (3, 13), (4, 5), (4, 7), (5, 6), (5, 8), (5, 7), (6, 7),
      ....: (8, 11), (8, 9), (8, 12), (9, 10), (9, 11), (9, 12), (10, 12)])
      sage: T = spqr_tree(G)
      sage: H = spqr_tree_to_graph(T)
      sage: H.is_isomorphic(G)
      True

  Python

      >>> from sage.all import *
      >>> from sage.graphs.connectivity import spqr_tree
      >>> from sage.graphs.connectivity import spqr_tree_to_graph
      >>> G = Graph([(Integer(1), Integer(2)), (Integer(1), Integer(4)), (Integer(1), Integer(8)), (Integer(1), Integer(12)), (Integer(3), Integer(4)), (Integer(2), Integer(3)),
      ... (Integer(2), Integer(13)), (Integer(3), Integer(13)), (Integer(4), Integer(5)), (Integer(4), Integer(7)), (Integer(5), Integer(6)), (Integer(5), Integer(8)), (Integer(5), Integer(7)), (Integer(6), Integer(7)),
      ... (Integer(8), Integer(11)), (Integer(8), Integer(9)), (Integer(8), Integer(12)), (Integer(9), Integer(10)), (Integer(9), Integer(11)), (Integer(9), Integer(12)), (Integer(10), Integer(12))])
      >>> T = spqr_tree(G)
      >>> H = spqr_tree_to_graph(T)
      >>> H.is_isomorphic(G)
      True

  A small multigraph

  Sage

      sage: G = Graph([(0, 2), (0, 2), (1, 3), (2, 3)], multiedges=True)
      sage: for i in range(3):
      ....:     G.add_clique([0, 1, G.add_vertex(), G.add_vertex()])
      sage: for i in range(3):
      ....:     G.add_clique([2, 3, G.add_vertex(), G.add_vertex()])
      sage: T = spqr_tree(G)
      sage: H = spqr_tree_to_graph(T)
      sage: H.is_isomorphic(G)
      True

  [Python]

      >>> from sage.all import *
      >>> G = Graph([(Integer(0), Integer(2)), (Integer(0), Integer(2)), (Integer(1), Integer(3)), (Integer(2), Integer(3))], multiedges=True)
      >>> for i in range(Integer(3)):
      ...     G.add_clique([Integer(0), Integer(1), G.add_vertex(), G.add_vertex()])
      >>> for i in range(Integer(3)):
      ...     G.add_clique([Integer(2), Integer(3), G.add_vertex(), G.add_vertex()])
      >>> T = spqr_tree(G)
      >>> H = spqr_tree_to_graph(T)
      >>> H.is_isomorphic(G)
      True

sage.graphs.connectivity.strong_articulation_points(G)

  Return the strong articulation points of this digraph.

  A vertex is a strong articulation point if its deletion increases the number of strongly connected components. This method implements the algorithm described in [ILS2012]. The time complexity is dominated by the time complexity of the immediate dominators finding algorithm.

  OUTPUT: the list of strong articulation points

  EXAMPLES:

  Two cliques sharing a vertex:

  Sage

      sage: from sage.graphs.connectivity import strong_articulation_points
      sage: D = digraphs.Complete(4)
      sage: D.add_clique([3, 4, 5, 6])
      sage: strong_articulation_points(D)
      [3]
      sage: D.strong_articulation_points()
      [3]

  Python

      >>> from sage.all import *
      >>> from sage.graphs.connectivity import strong_articulation_points
      >>> D = digraphs.Complete(Integer(4))
      >>> D.add_clique([Integer(3), Integer(4), Integer(5), Integer(6)])
      >>> strong_articulation_points(D)
      [3]
      >>> D.strong_articulation_points()
      [3]

  Two cliques connected by some arcs:

  Sage

      sage: D = digraphs.Complete(4) * 2
      sage: D.add_edges([(0, 4), (7, 3)])
      sage: sorted(strong_articulation_points(D))
      [0, 3, 4, 7]
      sage: D.add_edge(1, 5)
      sage: sorted(strong_articulation_points(D))
      [3, 7]
      sage: D.add_edge(6, 2)
      sage: strong_articulation_points(D)
      []

  [Python]

      >>> from sage.all import *
      >>> D = digraphs.Complete(Integer(4)) * Integer(2)
      >>> D.add_edges([(Integer(0), Integer(4)), (Integer(7), Integer(3))])
      >>> sorted(strong_articulation_points(D))
      [0, 3, 4, 7]
      >>> D.add_edge(Integer(1), Integer(5))
      >>> sorted(strong_articulation_points(D))
      [3, 7]
      >>> D.add_edge(Integer(6), Integer(2))
      >>> strong_articulation_points(D)
      []

  See also

  - `strongly_connected_components()`

  - `dominator_tree()`

sage.graphs.connectivity.strongly_connected_component_containing_vertex(G, v)

  Return the strongly connected component containing a given vertex.

  INPUT:

  - `G` – the input DiGraph

  - `v` – a vertex

  EXAMPLES:

  In the symmetric digraph of a graph, the strongly connected components are the connected components:

  Sage

      sage: from sage.graphs.connectivity import strongly_connected_component_containing_vertex
      sage: g = graphs.PetersenGraph()
      sage: d = DiGraph(g)
      sage: strongly_connected_component_containing_vertex(d, 0)
      [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
      sage: d.strongly_connected_component_containing_vertex(0)
      [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

  Python

      >>> from sage.all import *
      >>> from sage.graphs.connectivity import strongly_connected_component_containing_vertex
      >>> g = graphs.PetersenGraph()
      >>> d = DiGraph(g)
      >>> strongly_connected_component_containing_vertex(d, Integer(0))
      [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
      >>> d.strongly_connected_component_containing_vertex(Integer(0))
      [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

  Sage

      sage: g = DiGraph([(0, 1), (1, 0), (1, 2), (2, 3), (3, 2)])
      sage: strongly_connected_component_containing_vertex(g, 0)
      [0, 1]

  [Python]

      >>> from sage.all import *
      >>> g = DiGraph([(Integer(0), Integer(1)), (Integer(1), Integer(0)), (Integer(1), Integer(2)), (Integer(2), Integer(3)), (Integer(3), Integer(2))])
      >>> strongly_connected_component_containing_vertex(g, Integer(0))
      [0, 1]

sage.graphs.connectivity.strongly_connected_components_digraph(G, keep_labels=False)

  Return the digraph of the strongly connected components.

  The digraph of the strongly connected components of a graph \(G\) has a vertex per strongly connected component included in \(G\). There is an edge from a component \(C_1\) to a component \(C_2\) if there is an edge in \(G\) from a vertex \(u_1 \in C_1\) to a vertex \(u_2 \in C_2\).

  INPUT:

  - `G` – the input DiGraph

  - `keep_labels` – boolean (default: `False`); when `keep_labels=True`, the resulting digraph has an edge from a component \(C_i\) to a component \(C_j\) for each edge in \(G\) from a vertex \(u_i \in C_i\) to a vertex \(u_j \in C_j\). Hence the resulting digraph may have loops and multiple edges. However, edges in the result with same source, target, and label are not duplicated (see examples below). When `keep_labels=False`, the return digraph is simple, so without loops nor multiple edges, and edges are unlabelled.

  EXAMPLES:

  Such a digraph is always acyclic:

  Sage

      sage: from sage.graphs.connectivity import strongly_connected_components_digraph
      sage: g = digraphs.RandomDirectedGNP(15, .1)
      sage: scc_digraph = strongly_connected_components_digraph(g)
      sage: scc_digraph.is_directed_acyclic()
      True
      sage: scc_digraph = g.strongly_connected_components_digraph()
      sage: scc_digraph.is_directed_acyclic()
      True

  Python

      >>> from sage.all import *
      >>> from sage.graphs.connectivity import strongly_connected_components_digraph
      >>> g = digraphs.RandomDirectedGNP(Integer(15), RealNumber('.1'))
      >>> scc_digraph = strongly_connected_components_digraph(g)
      >>> scc_digraph.is_directed_acyclic()
      True
      >>> scc_digraph = g.strongly_connected_components_digraph()
      >>> scc_digraph.is_directed_acyclic()
      True

  The vertices of the digraph of strongly connected components are exactly the strongly connected components:

  Sage

      sage: g = digraphs.ButterflyGraph(2)
      sage: scc_digraph = strongly_connected_components_digraph(g)
      sage: g.is_directed_acyclic()
      True
      sage: V_scc = list(scc_digraph)
      sage: all(Set(scc) in V_scc for scc in g.strongly_connected_components())
      True

  [Python]

      >>> from sage.all import *
      >>> g = digraphs.ButterflyGraph(Integer(2))
      >>> scc_digraph = strongly_connected_components_digraph(g)
      >>> g.is_directed_acyclic()
      True
      >>> V_scc = list(scc_digraph)
      >>> all(Set(scc) in V_scc for scc in g.strongly_connected_components())
      True

  The following digraph has three strongly connected components, and the digraph of those is a `TransitiveTournament()`:

  Sage

      sage: g = DiGraph({0: {1: "01", 2: "02", 3: "03"}, 1: {2: "12"}, 2:{1: "21", 3: "23"}})
      sage: scc_digraph = strongly_connected_components_digraph(g)
      sage: scc_digraph.is_isomorphic(digraphs.TransitiveTournament(3))
      True

  Python

      >>> from sage.all import *
      >>> g = DiGraph({Integer(0): {Integer(1): "01", Integer(2): "02", Integer(3): "03"}, Integer(1): {Integer(2): "12"}, Integer(2):{Integer(1): "21", Integer(3): "23"}})
      >>> scc_digraph = strongly_connected_components_digraph(g)
      >>> scc_digraph.is_isomorphic(digraphs.TransitiveTournament(Integer(3)))
      True

  By default, the labels are discarded, and the result has no loops nor multiple edges. If `keep_labels` is `True`, then the labels are kept, and the result is a multi digraph, possibly with multiple edges and loops. However, edges in the result with same source, target, and label are not duplicated (see the edges from 0 to the strongly connected component \(\{1,2\}\) below):

  Sage

      sage: g = DiGraph({0: {1: "0-12", 2: "0-12", 3: "0-3"}, 1: {2: "1-2", 3: "1-3"}, 2: {1: "2-1", 3: "2-3"}})
      sage: g.order(), g.size()
      (4, 7)
      sage: scc_digraph = strongly_connected_components_digraph(g, keep_labels=True)
      sage: (scc_digraph.order(), scc_digraph.size())
      (3, 6)
      sage: set(g.edge_labels()) == set(scc_digraph.edge_labels())
      True

  [Python]

      >>> from sage.all import *
      >>> g = DiGraph({Integer(0): {Integer(1): "0-12", Integer(2): "0-12", Integer(3): "0-3"}, Integer(1): {Integer(2): "1-2", Integer(3): "1-3"}, Integer(2): {Integer(1): "2-1", Integer(3): "2-3"}})
      >>> g.order(), g.size()
      (4, 7)
      >>> scc_digraph = strongly_connected_components_digraph(g, keep_labels=True)
      >>> (scc_digraph.order(), scc_digraph.size())
      (3, 6)
      >>> set(g.edge_labels()) == set(scc_digraph.edge_labels())
      True

sage.graphs.connectivity.strongly_connected_components_subgraphs(G, immutable=None)

  Return the strongly connected components as a list of subgraphs.

  INPUT:

  - `G` – a directed graph

  - `immutable` – boolean (default: `None`); whether to create a mutable/immutable graphs. `immutable=None` (default) means that the graph and the subgraphs of its connected components will behave the same way.

  EXAMPLES:

  In the symmetric digraph of a graph, the strongly connected components are the connected components:

  Sage

      sage: from sage.graphs.connectivity import strongly_connected_components_subgraphs
      sage: g = graphs.PetersenGraph()
      sage: d = DiGraph(g)
      sage: strongly_connected_components_subgraphs(d)
      [Subgraph of (Petersen graph): Digraph on 10 vertices]
      sage: d.strongly_connected_components_subgraphs()
      [Subgraph of (Petersen graph): Digraph on 10 vertices]

  Python

      >>> from sage.all import *
      >>> from sage.graphs.connectivity import strongly_connected_components_subgraphs
      >>> g = graphs.PetersenGraph()
      >>> d = DiGraph(g)
      >>> strongly_connected_components_subgraphs(d)
      [Subgraph of (Petersen graph): Digraph on 10 vertices]
      >>> d.strongly_connected_components_subgraphs()
      [Subgraph of (Petersen graph): Digraph on 10 vertices]

  Sage

      sage: g = DiGraph([(0, 1), (1, 0), (1, 2), (2, 3), (3, 2)])
      sage: strongly_connected_components_subgraphs(g)
      [Subgraph of (): Digraph on 2 vertices, Subgraph of (): Digraph on 2 vertices]

  [Python]

      >>> from sage.all import *
      >>> g = DiGraph([(Integer(0), Integer(1)), (Integer(1), Integer(0)), (Integer(1), Integer(2)), (Integer(2), Integer(3)), (Integer(3), Integer(2))])
      >>> strongly_connected_components_subgraphs(g)
      [Subgraph of (): Digraph on 2 vertices, Subgraph of (): Digraph on 2 vertices]

sage.graphs.connectivity.vertex_connectivity(G, value_only=True, sets=False, k=None, solver=None, verbose=0, integrality_tolerance=0.001)

  Return the vertex connectivity of the graph.

  For more information, see the Wikipedia article Connectivity_(graph_theory) and the Wikipedia article K-vertex-connected_graph.

  Note

  - When the graph is directed, this method actually computes the strong connectivity, (i.e. a directed graph is strongly \(k\)-connected if there are \(k\) vertex disjoint paths between any two vertices \(u, v\)). If you do not want to consider strong connectivity, the best is probably to convert your `DiGraph` object to a `Graph` object, and compute the connectivity of this other graph.

  - By convention, a complete graph on \(n\) vertices is \(n-1\) connected. In this case, no certificate can be given as there is no pair of vertices split by a cut of order \(k-1\). For this reason, the certificates returned in this situation are empty.

  INPUT:

  - `G` – the input Sage (Di)Graph

  - `value_only` – boolean (default: `True`)

    - When set to `True` (default), only the value is returned.

    - When set to `False`, both the value and a minimum vertex cut are returned.

  - `sets` – boolean (default: `False`); whether to also return the two

      sets of vertices that are disconnected by the cut (implies `value_only=False`)

  - `k` – integer (default: `None`); when specified, check if the vertex connectivity of the (di)graph is larger or equal to \(k\). The method thus outputs a boolean only.

  - `solver` – string (default: `None`); specifies a Mixed Integer Linear Programming (MILP) solver to be used. If set to `None`, the default one is used. For more information on MILP solvers and which default solver is used, see the method `solve` of the class `MixedIntegerLinearProgram`.

  - `verbose` – integer (default: 0); sets the level of verbosity. Set to 0 by default, which means quiet.

  - `integrality_tolerance` – float; parameter for use with MILP solvers over an inexact base ring; see `MixedIntegerLinearProgram.get_values()`.

  EXAMPLES:

  A basic application on a `PappusGraph`:

  Sage

      sage: from sage.graphs.connectivity import vertex_connectivity
      sage: g = graphs.PappusGraph()
      sage: vertex_connectivity(g)                                                     # needs sage.numerical.mip
      3
      sage: g.vertex_connectivity()                                                    # needs sage.numerical.mip
      3

  Python

      >>> from sage.all import *
      >>> from sage.graphs.connectivity import vertex_connectivity
      >>> g = graphs.PappusGraph()
      >>> vertex_connectivity(g)                                                     # needs sage.numerical.mip
      3
      >>> g.vertex_connectivity()                                                    # needs sage.numerical.mip
      3

  In a grid, the vertex connectivity is equal to the minimum degree, in which case one of the two sets is of cardinality \(1\):

  Sage

      sage: g = graphs.GridGraph([ 3,3 ])
      sage: [value, cut, [ setA, setB ]] = vertex_connectivity(g, sets=True)           # needs sage.numerical.mip
      sage: len(setA) == 1 or len(setB) == 1                                           # needs sage.numerical.mip
      True

  [Python]

      >>> from sage.all import *
      >>> g = graphs.GridGraph([ Integer(3),Integer(3) ])
      >>> [value, cut, [ setA, setB ]] = vertex_connectivity(g, sets=True)           # needs sage.numerical.mip
      >>> len(setA) == Integer(1) or len(setB) == Integer(1)                                           # needs sage.numerical.mip
      True

  A vertex cut in a tree is any internal vertex:

  Sage

      sage: tree = graphs.RandomTree(15)
      sage: val, [cut_vertex] = vertex_connectivity(tree, value_only=False)            # needs sage.numerical.mip
      sage: tree.degree(cut_vertex) > 1                                                # needs sage.numerical.mip
      True

  Python

      >>> from sage.all import *
      >>> tree = graphs.RandomTree(Integer(15))
      >>> val, [cut_vertex] = vertex_connectivity(tree, value_only=False)            # needs sage.numerical.mip
      >>> tree.degree(cut_vertex) > Integer(1)                                                # needs sage.numerical.mip
      True

  When `value_only = True`, this function is optimized for small connectivity values and does not need to build a linear program.

  It is the case for connected graphs which are not connected:

  Sage

      sage: g = 2 * graphs.PetersenGraph()
      sage: vertex_connectivity(g)                                                     # needs sage.numerical.mip
      0

  [Python]

      >>> from sage.all import *
      >>> g = Integer(2) * graphs.PetersenGraph()
      >>> vertex_connectivity(g)                                                     # needs sage.numerical.mip
      0

  Or if they are just 1-connected:

  Sage

      sage: g = graphs.PathGraph(10)
      sage: vertex_connectivity(g)                                                     # needs sage.numerical.mip
      1

  Python

      >>> from sage.all import *
      >>> g = graphs.PathGraph(Integer(10))
      >>> vertex_connectivity(g)                                                     # needs sage.numerical.mip
      1

  For directed graphs, the strong connectivity is tested through the dedicated function:

  Sage

      sage: g = digraphs.ButterflyGraph(3)
      sage: vertex_connectivity(g)                                                     # needs sage.numerical.mip
      0

  [Python]

      >>> from sage.all import *
      >>> g = digraphs.ButterflyGraph(Integer(3))
      >>> vertex_connectivity(g)                                                     # needs sage.numerical.mip
      0

  A complete graph on \(10\) vertices is \(9\)-connected:

  Sage

      sage: g = graphs.CompleteGraph(10)
      sage: vertex_connectivity(g)                                                     # needs sage.numerical.mip
      9

  Python

      >>> from sage.all import *
      >>> g = graphs.CompleteGraph(Integer(10))
      >>> vertex_connectivity(g)                                                     # needs sage.numerical.mip
      9

  A complete digraph on \(10\) vertices is \(9\)-connected:

  Sage

      sage: g = DiGraph(graphs.CompleteGraph(10))
      sage: vertex_connectivity(g)                                                     # needs sage.numerical.mip
      9

  [Python]

      >>> from sage.all import *
      >>> g = DiGraph(graphs.CompleteGraph(Integer(10)))
      >>> vertex_connectivity(g)                                                     # needs sage.numerical.mip
      9

  When parameter `k` is set, we only check for the existence of a vertex cut of order at least `k`:

  Sage

      sage: g = graphs.PappusGraph()
      sage: vertex_connectivity(g, k=3)                                                # needs sage.numerical.mip
      True
      sage: vertex_connectivity(g, k=4)                                                # needs sage.numerical.mip
      False

  Python

      >>> from sage.all import *
      >>> g = graphs.PappusGraph()
      >>> vertex_connectivity(g, k=Integer(3))                                                # needs sage.numerical.mip
      True
      >>> vertex_connectivity(g, k=Integer(4))                                                # needs sage.numerical.mip
      False