25
25
Methods
26
26
-------
27
27
"""
28
+ # ****************************************************************************
29
+ # Copyright (C) 2017 Kolja Knauer <[email protected] >
30
+ # 2017 Petru Valicov <[email protected] >
31
+ # 2017-2023 David Coudert <[email protected] >
32
+ #
33
+ # This program is free software: you can redistribute it and/or modify
34
+ # it under the terms of the GNU General Public License as published by
35
+ # the Free Software Foundation, either version 2 of the License, or
36
+ # (at your option) any later version.
37
+ # https://www.gnu.org/licenses/
38
+ # ****************************************************************************
39
+
28
40
from copy import copy
29
41
from sage .graphs .digraph import DiGraph
30
42
31
43
32
44
def strong_orientations_iterator (G ):
33
45
r"""
34
- Returns an iterator over all strong orientations of a graph `G`.
46
+ Return an iterator over all strong orientations of a graph `G`.
35
47
36
- A strong orientation of a graph is an orientation of its edges such that
37
- the obtained digraph is strongly connected (i.e. there exist a directed path
38
- between each pair of vertices).
48
+ A strong orientation of a graph is an orientation of its edges such that the
49
+ obtained digraph is strongly connected (i.e. there exist a directed path
50
+ between each pair of vertices). According Robin's theorem (see the
51
+ :wikipedia:`Robbins_theorem`), the graphs that have strong orientations are
52
+ exactly the 2-edge-connected graphs (i.e., the bridgeless graphs).
39
53
40
54
ALGORITHM:
41
55
@@ -84,7 +98,7 @@ def strong_orientations_iterator(G):
84
98
85
99
A tree cannot be strongly oriented::
86
100
87
- sage: g = graphs.RandomTree(100 )
101
+ sage: g = graphs.RandomTree(10 )
88
102
sage: len(list(g.strong_orientations_iterator()))
89
103
0
90
104
@@ -115,44 +129,37 @@ def strong_orientations_iterator(G):
115
129
sage: g = graphs.PetersenGraph()
116
130
sage: nr1 = len(list(g.strong_orientations_iterator()))
117
131
sage: nr2 = g.tutte_polynomial()(0,2)
118
- sage: nr1 == nr2/2 # The Tutte polynomial counts also the symmetrical orientations
132
+ sage: nr1 == nr2/2 # The Tutte polynomial counts also the symmetrical orientations
119
133
True
120
-
121
134
"""
122
135
# if the graph has a bridge or is disconnected,
123
136
# then it cannot be strongly oriented
124
- if G .order () < 3 or not G .is_biconnected ( ):
137
+ if G .order () < 3 or not G .is_connected () or any ( G . bridges ( labels = False ) ):
125
138
return
126
139
127
- V = G .vertices (sort = False )
128
- Dg = DiGraph ([V , G .edges (sort = False )], pos = G .get_pos ())
140
+ V = list (G )
129
141
130
142
# compute an arbitrary spanning tree of the undirected graph
131
- te = G .min_spanning_tree ()
132
- treeEdges = [(u , v ) for u , v , _ in te ]
133
- tree_edges_set = set (treeEdges )
134
- A = [edge for edge in G .edge_iterator (labels = False ) if edge not in tree_edges_set ]
143
+ T = G .subgraph (vertices = G , edges = G .min_spanning_tree (), inplace = False )
144
+ treeEdges = list (T .edges (labels = False , sort = False ))
145
+ A = [edge for edge in G .edge_iterator (labels = False ) if not T .has_edge (edge )]
146
+
147
+ # Initialize a digraph with the edges of the spanning tree doubly oriented
148
+ Dg = T .to_directed (sparse = True )
149
+ Dg .add_edges (A )
135
150
136
151
# initialization of the first binary word 00...0
137
152
# corresponding to the current orientation of the non-tree edges
138
153
existingAedges = [0 ] * len (A )
139
154
140
- # Make the edges of the spanning tree doubly oriented
141
- for e in treeEdges :
142
- if Dg .has_edge (e ):
143
- Dg .add_edge (e [1 ], e [0 ])
144
- else :
145
- Dg .add_edge (e )
146
-
147
155
# Generate all orientations for non-tree edges (using Gray code)
148
156
# Each of these orientations can be extended to a strong orientation
149
157
# of G by orienting properly the tree-edges
150
158
previousWord = 0
151
- i = 0
152
159
153
160
# the orientation of one edge is fixed so we consider one edge less
154
161
nr = 2 ** (len (A ) - 1 )
155
- while i < nr :
162
+ for i in range ( nr ) :
156
163
word = (i >> 1 ) ^ i
157
164
bitChanged = word ^ previousWord
158
165
@@ -171,7 +178,6 @@ def strong_orientations_iterator(G):
171
178
# launch the algorithm for enumeration of the solutions
172
179
for sol in _strong_orientations_of_a_mixed_graph (Dg , V , treeEdges ):
173
180
yield sol
174
- i = i + 1
175
181
176
182
177
183
def _strong_orientations_of_a_mixed_graph (Dg , V , E ):
@@ -201,9 +207,9 @@ def _strong_orientations_of_a_mixed_graph(Dg, V, E):
201
207
202
208
sage: from sage.graphs.orientations import _strong_orientations_of_a_mixed_graph
203
209
sage: g = graphs.CycleGraph(5)
204
- sage: Dg = DiGraph(g) # all edges of g will be doubly oriented
210
+ sage: Dg = DiGraph(g) # all edges of g will be doubly oriented
205
211
sage: it = _strong_orientations_of_a_mixed_graph(Dg, list(g), list(g.edges(labels=False, sort=False)))
206
- sage: len(list(it)) # there are two orientations of this multigraph
212
+ sage: len(list(it)) # there are two orientations of this multigraph
207
213
2
208
214
"""
209
215
length = len (E )
@@ -213,7 +219,9 @@ def _strong_orientations_of_a_mixed_graph(Dg, V, E):
213
219
u , v = E [i ]
214
220
Dg .delete_edge (u , v )
215
221
if not (v in Dg .depth_first_search (u )):
216
- del E [i ]
222
+ # del E[i] in constant time
223
+ E [i ] = E [- 1 ]
224
+ E .pop ()
217
225
length -= 1
218
226
Dg .add_edge (u , v )
219
227
Dg .delete_edge (v , u )
@@ -222,7 +230,9 @@ def _strong_orientations_of_a_mixed_graph(Dg, V, E):
222
230
Dg .add_edge (u , v )
223
231
Dg .delete_edge (v , u )
224
232
if not (u in Dg .depth_first_search (v )):
225
- del E [i ]
233
+ # del E[i] in constant time
234
+ E [i ] = E [- 1 ]
235
+ E .pop ()
226
236
length -= 1
227
237
boundEdges .append ((u , v ))
228
238
Dg .delete_edge (u , v )
0 commit comments