Skip to content

Commit

Permalink
gh-38435: add method is_edge_cut to graphs
Browse files Browse the repository at this point in the history
    
Similarly to #38418, this PR adds a method to check whether a set of
edges forms an edge cut of the (di)graph, that is if the removal of
these edges increases the number of (weakly) connected components.


### 📝 Checklist

<!-- Put an `x` in all the boxes that apply. -->

- [x] The title is concise and informative.
- [x] The description explains in detail what this PR is about.
- [x] I have linked a relevant issue or discussion.
- [x] I have created tests covering the changes.
- [x] I have updated the documentation and checked the documentation
preview.

### ⌛ Dependencies

<!-- List all open PRs that this PR logically depends on. For example,
-->
<!-- - #12345: short description why this is a dependency -->
<!-- - #34567: ... -->
    
URL: #38435
Reported by: David Coudert
Reviewer(s): David Coudert, Kwankyu Lee
  • Loading branch information
Release Manager committed Sep 13, 2024
2 parents 76fbb6c + 56f380b commit 473a571
Show file tree
Hide file tree
Showing 2 changed files with 156 additions and 18 deletions.
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.
: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 ``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: 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().
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
"""
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 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
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 the input edges form an edge cut.
:meth:`~GenericGraph.is_cut_vertex` | Return ``True`` if the input vertex is a cut-vertex.
: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 @@ -25000,6 +25001,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

0 comments on commit 473a571

Please sign in to comment.