Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add method is_edge_cut to graphs #38435

Merged
merged 12 commits into from
Sep 15, 2024
170 changes: 153 additions & 17 deletions src/sage/graphs/connectivity.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ Here is what the module can do:
:meth:`connected_components_sizes` | Return the sizes of the connected components as a list.
:meth:`blocks_and_cut_vertices` | Return the blocks and cut vertices of the graph.
:meth:`blocks_and_cuts_tree` | Return the blocks-and-cuts tree of the graph.
:meth:`is_cut_edge` | Return ``True`` if the input edge is a cut-edge or a bridge.
:meth:`is_cut_edge` | Check whether the input edge is a cut-edge or a bridge.
:meth:`is_edge_cut` | Check whether the input edges form an edge cut.
:meth:`is_cut_vertex` | Check whether the input vertex is a cut-vertex.
dcoudert marked this conversation as resolved.
Show resolved Hide resolved
:meth:`edge_connectivity` | Return the edge connectivity of the graph.
:meth:`vertex_connectivity` | Return the vertex connectivity of the graph.
Expand Down Expand Up @@ -70,6 +71,7 @@ Methods
# ****************************************************************************

from sage.misc.superseded import deprecation
from sage.sets.disjoint_set cimport DisjointSet


def is_connected(G):
Expand Down Expand Up @@ -732,13 +734,160 @@ def blocks_and_cuts_tree(G):
return g


def is_edge_cut(G, edges):
"""
Check whether the input edges form an edge cut.

dcoudert marked this conversation as resolved.
Show resolved Hide resolved
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

dcoudert marked this conversation as resolved.
Show resolved Hide resolved
EXAMPLES:

A cycle graph of order 4::

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

A pending edge is a cut-edge::

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

Edge labels are ignored, even if specified::

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

The graph can have loops::

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

Multiple edges are not allowed::

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().

kwankyu marked this conversation as resolved.
Show resolved Hide resolved
An error is raised if an element of ``edges`` is not an edge of `G`::

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

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

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

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

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
"""
dcoudert marked this conversation as resolved.
Show resolved Hide resolved
G._scream_if_not_simple(allow_loops=True)

cdef set C = set() # set of edges of the potential cut
cdef set S = set() # set of incident vertices
for e in edges:
u, v = e[0], e[1]
if not G.has_edge(u, v):
raise ValueError("edge {0} is not an edge of the graph".format(repr(e)))
if u == v:
# We ignore loops
continue
if G.degree(u) == 1 or G.degree(v) == 1:
# e is a pending edge and so a cut-edge
return True
S.add(u)
S.add(v)
C.add((u, v))
if not G.is_directed():
C.add((v, u))

cdef list queue
cdef set seen
DS = DisjointSet(G)

for comp in G.connected_components():
if not S.intersection(comp):
# This component is not involved in the cut
continue

# We run a DFS in comp from any vertex and avoid edges in C
start = comp[0]
queue = [start]
seen = set(queue)
while queue:
v = queue.pop()
for e in G.edge_iterator(vertices=[v], labels=False, ignore_direction=True, sort_vertices=False):
if e in C:
continue
w = e[1] if e[0] == v else e[0]
if w not in seen:
seen.add(w)
DS.union(v, w)
queue.append(w)

# We now check if some vertices of comp have not been reached
if len(set(DS.find(v) for v in comp)) > 1:
return True

return False


def is_cut_edge(G, u, v=None, label=None):
"""
Return ``True`` if the input edge is a cut-edge or a bridge.
Check whether the input edge is a cut-edge or a bridge.

dcoudert marked this conversation as resolved.
Show resolved Hide resolved
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
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.

Expand Down Expand Up @@ -787,20 +936,7 @@ def is_cut_edge(G, u, v=None, label=None):
Traceback (most recent call last):
...
ValueError: edge not in graph

TESTS:

If ``G`` is not a Sage graph, an error is raised::

sage: is_cut_edge('I am not a graph',0)
Traceback (most recent call last):
...
TypeError: the input must be a Sage graph
"""
from sage.graphs.generic_graph import GenericGraph
if not isinstance(G, GenericGraph):
raise TypeError("the input must be a Sage graph")

if label is None:
if v is None:
try:
Expand Down
4 changes: 3 additions & 1 deletion src/sage/graphs/generic_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,8 @@
:meth:`~GenericGraph.connected_components_sizes` | Return the sizes of the connected components as a list.
:meth:`~GenericGraph.blocks_and_cut_vertices` | Compute the blocks and cut vertices of the graph.
:meth:`~GenericGraph.blocks_and_cuts_tree` | Compute the blocks-and-cuts tree of the graph.
:meth:`~GenericGraph.is_cut_edge` | Return ``True`` if the input edge is a cut-edge or a bridge.
:meth:`~GenericGraph.is_cut_edge` | Check whether the input edge is a cut-edge or a bridge.
:meth:`~GenericGraph.is_edge_cut` | Check whether ``edges`` is an edge cut of ``G``.
:meth:`~GenericGraph.is_cut_vertex` | Return ``True`` if the input vertex is a cut-vertex.
kwankyu marked this conversation as resolved.
Show resolved Hide resolved
:meth:`~GenericGraph.edge_cut` | Return a minimum edge cut between vertices `s` and `t`
:meth:`~GenericGraph.vertex_cut` | Return a minimum vertex cut between non-adjacent vertices `s` and `t`
Expand Down Expand Up @@ -24997,6 +24998,7 @@ def is_self_complementary(self):
from sage.graphs.connectivity import blocks_and_cut_vertices
from sage.graphs.connectivity import blocks_and_cuts_tree
from sage.graphs.connectivity import is_cut_edge
from sage.graphs.connectivity import is_edge_cut
from sage.graphs.connectivity import is_cut_vertex
from sage.graphs.connectivity import edge_connectivity
from sage.graphs.connectivity import vertex_connectivity
Expand Down
Loading