@@ -23,7 +23,8 @@ Here is what the module can do:
23
23
:meth:`connected_components_sizes` | Return the sizes of the connected components as a list.
24
24
:meth:`blocks_and_cut_vertices` | Return the blocks and cut vertices of the graph.
25
25
: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.
27
28
:meth:`is_cut_vertex` | Check whether the input vertex is a cut-vertex.
28
29
:meth:`edge_connectivity` | Return the edge connectivity of the graph.
29
30
:meth:`vertex_connectivity` | Return the vertex connectivity of the graph.
@@ -70,6 +71,7 @@ Methods
70
71
# ****************************************************************************
71
72
72
73
from sage.misc.superseded import deprecation
74
+ from sage.sets.disjoint_set cimport DisjointSet
73
75
74
76
75
77
def is_connected (G ):
@@ -732,13 +734,160 @@ def blocks_and_cuts_tree(G):
732
734
return g
733
735
734
736
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
+
735
884
def is_cut_edge (G , u , v = None , label = None ):
736
885
"""
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`` .
738
887
739
888
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
742
891
a digraph, a cut edge is an edge that when removed increases
743
892
the number of (weakly) connected components.
744
893
@@ -787,20 +936,7 @@ def is_cut_edge(G, u, v=None, label=None):
787
936
Traceback (most recent call last):
788
937
...
789
938
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
799
939
"""
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
-
804
940
if label is None :
805
941
if v is None :
806
942
try :
0 commit comments