@@ -23,7 +23,8 @@ Here is what the module can do:
2323 :meth:`connected_components_sizes` | Return the sizes of the connected components as a list.
2424 :meth:`blocks_and_cut_vertices` | Return the blocks and cut vertices of the graph.
2525 :meth:`blocks_and_cuts_tree` | Return the blocks-and-cuts tree of the graph.
26- :meth:`is_cut_edge` | Return ``True`` if the input edge is a cut-edge or a bridge.
26+ :meth:`is_cut_edge` | Check whether the input edge is a cut-edge or a bridge.
27+ :meth:`is_edge_cut` | Check whether the input edges form an edge cut.
2728 :meth:`is_cut_vertex` | Check whether the input vertex is a cut-vertex.
2829 :meth:`edge_connectivity` | Return the edge connectivity of the graph.
2930 :meth:`vertex_connectivity` | Return the vertex connectivity of the graph.
@@ -70,6 +71,7 @@ Methods
7071# ****************************************************************************
7172
7273from sage.misc.superseded import deprecation
74+ from sage.sets.disjoint_set cimport DisjointSet
7375
7476
7577def is_connected (G ):
@@ -732,13 +734,160 @@ def blocks_and_cuts_tree(G):
732734 return g
733735
734736
737+ def is_edge_cut (G , edges ):
738+ """
739+ Check whether ``edges`` form an edge cut.
740+
741+ A set of edges is an edge cut of a graph if its removal increases the number
742+ of connected components. In a digraph, we consider the number of (weakly)
743+ connected components.
744+
745+ This method is not working for (di)graphs with multiple edges. Furthermore,
746+ edge labels are ignored.
747+
748+ INPUT:
749+
750+ - ``G`` -- a (di)graph
751+
752+ - ``edges`` -- a set of edges
753+
754+ EXAMPLES:
755+
756+ A cycle graph of order 4::
757+
758+ sage: from sage.graphs.connectivity import is_edge_cut
759+ sage: G = graphs.CycleGraph(4)
760+ sage: is_edge_cut(G, [(1, 2)])
761+ False
762+ sage: is_edge_cut(G, [(1, 2), (2, 3)])
763+ True
764+ sage: is_edge_cut(G, [(1, 2), (3, 0)])
765+ True
766+
767+ A pending edge is a cut-edge::
768+
769+ sage: G.add_edge((0, 5, 'silly'))
770+ sage: is_edge_cut(G, [(0, 5, 'silly')])
771+ True
772+
773+ Edge labels are ignored, even if specified::
774+
775+ sage: G.add_edge((2, 5, 'xyz'))
776+ sage: is_edge_cut(G, [(0, 5), (2, 5)])
777+ True
778+ sage: is_edge_cut(G, [(0, 5), (2, 5, 'xyz')])
779+ True
780+ sage: is_edge_cut(G, [(0, 5, 'silly'), (2, 5)])
781+ True
782+ sage: is_edge_cut(G, [(0, 5, 'aa'), (2, 5, 'bb')])
783+ True
784+
785+ The graph can have loops::
786+
787+ sage: G.allow_loops(True)
788+ sage: G.add_edge(0, 0)
789+ sage: is_edge_cut(G, [(0, 5), (2, 5)])
790+ True
791+ sage: is_edge_cut(G, [(0, 0), (0, 5), (2, 5)])
792+ True
793+
794+ Multiple edges are not allowed::
795+
796+ sage: G.allow_multiple_edges(True)
797+ sage: is_edge_cut(G, [(0, 5), (2, 5)])
798+ Traceback (most recent call last):
799+ ...
800+ ValueError: This method is not known to work on graphs with
801+ multiedges. Perhaps this method can be updated to handle them, but in
802+ the meantime if you want to use it please disallow multiedges using
803+ allow_multiple_edges().
804+
805+ An error is raised if an element of ``edges`` is not an edge of `G`::
806+
807+ sage: G = graphs.CycleGraph(4)
808+ sage: is_edge_cut(G, [(0, 2)])
809+ Traceback (most recent call last):
810+ ...
811+ ValueError: edge (0, 2) is not an edge of the graph
812+
813+ For digraphs, this method considers the number of (weakly) connected
814+ components::
815+
816+ sage: G = digraphs.Circuit(4)
817+ sage: is_edge_cut(G, [(0, 1)])
818+ False
819+ sage: G = digraphs.Circuit(4)
820+ sage: is_edge_cut(G, [(0, 1), (1, 2)])
821+ True
822+
823+ For disconnected (di)graphs, the method checks if the number of (weakly)
824+ connected components increases::
825+
826+ sage: G = graphs.CycleGraph(4) * 2
827+ sage: is_edge_cut(G, [(1, 2), (2, 3)])
828+ True
829+ sage: G = digraphs.Circuit(4) * 2
830+ sage: is_edge_cut(G, [(0, 1), (1, 2)])
831+ True
832+ """
833+ G._scream_if_not_simple(allow_loops = True )
834+
835+ cdef set C = set () # set of edges of the potential cut
836+ cdef set S = set () # set of incident vertices
837+ for e in edges:
838+ u, v = e[0 ], e[1 ]
839+ if not G.has_edge(u, v):
840+ raise ValueError (" edge {0} is not an edge of the graph" .format(repr (e)))
841+ if u == v:
842+ # We ignore loops
843+ continue
844+ if G.degree(u) == 1 or G.degree(v) == 1 :
845+ # e is a pending edge and so a cut-edge
846+ return True
847+ S.add(u)
848+ S.add(v)
849+ C.add((u, v))
850+ if not G.is_directed():
851+ C.add((v, u))
852+
853+ cdef list queue
854+ cdef set seen
855+ DS = DisjointSet(G)
856+
857+ for comp in G.connected_components():
858+ if not S.intersection(comp):
859+ # This component is not involved in the cut
860+ continue
861+
862+ # We run a DFS in comp from any vertex and avoid edges in C
863+ start = comp[0 ]
864+ queue = [start]
865+ seen = set (queue)
866+ while queue:
867+ v = queue.pop()
868+ for e in G.edge_iterator(vertices = [v], labels = False , ignore_direction = True , sort_vertices = False ):
869+ if e in C:
870+ continue
871+ w = e[1 ] if e[0 ] == v else e[0 ]
872+ if w not in seen:
873+ seen.add(w)
874+ DS.union(v, w)
875+ queue.append(w)
876+
877+ # We now check if some vertices of comp have not been reached
878+ if len (set (DS.find(v) for v in comp)) > 1 :
879+ return True
880+
881+ return False
882+
883+
735884def is_cut_edge (G , u , v = None , label = None ):
736885 """
737- Return ``True`` if the input edge is a cut-edge or a bridge.
886+ Check whether the edge ``(u, v)`` is a cut-edge or a bridge of graph ``G`` .
738887
739888 A cut edge (or bridge) is an edge that when removed increases
740- the number of connected components. This function works with
741- simple graphs as well as graphs with loops and multiedges. In
889+ the number of connected components. This function works with
890+ simple graphs as well as graphs with loops and multiedges. In
742891 a digraph, a cut edge is an edge that when removed increases
743892 the number of (weakly) connected components.
744893
@@ -787,20 +936,7 @@ def is_cut_edge(G, u, v=None, label=None):
787936 Traceback (most recent call last):
788937 ...
789938 ValueError: edge not in graph
790-
791- TESTS:
792-
793- If ``G`` is not a Sage graph, an error is raised::
794-
795- sage: is_cut_edge('I am not a graph',0)
796- Traceback (most recent call last):
797- ...
798- TypeError: the input must be a Sage graph
799939 """
800- from sage.graphs.generic_graph import GenericGraph
801- if not isinstance (G, GenericGraph):
802- raise TypeError (" the input must be a Sage graph" )
803-
804940 if label is None :
805941 if v is None :
806942 try :
0 commit comments