Skip to content

Commit e86a48f

Browse files
author
Release Manager
committed
gh-35981: some improvements in sage/graphs/orientations.py We do some improvements in file `src/sage/graphs/orientations.py` and in particular in method `strong_orientations_iterator`. The next step will be to move all methods related to orientations from `graph.py` to `orientations.py`. ### 📝 Checklist <!-- Put an `x` in all the boxes that apply. --> <!-- If your change requires a documentation PR, please link it appropriately --> <!-- If you're unsure about any of these, don't hesitate to ask. We're here to help! --> <!-- Feel free to remove irrelevant items. --> - [x] The title is concise, informative, and self-explanatory. - [x] The description explains in detail what this PR is about. - [ ] I have linked a relevant issue or discussion. - [ ] I have created tests covering the changes. - [x] I have updated the documentation accordingly. ### ⌛ Dependencies <!-- List all open PRs that this PR logically depends on - #12345: short description why this is a dependency - #34567: ... --> <!-- If you're unsure about any of these, don't hesitate to ask. We're here to help! --> URL: #35981 Reported by: David Coudert Reviewer(s): David Coudert, Matthias Köppe
2 parents df198d0 + 80fc1f3 commit e86a48f

File tree

1 file changed

+39
-30
lines changed

1 file changed

+39
-30
lines changed

src/sage/graphs/orientations.py

Lines changed: 39 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,31 @@
2525
Methods
2626
-------
2727
"""
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+
2840
from copy import copy
2941
from sage.graphs.digraph import DiGraph
3042

3143

3244
def strong_orientations_iterator(G):
3345
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`.
3547
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 to Robbins' 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).
3953
4054
ALGORITHM:
4155
@@ -84,7 +98,7 @@ def strong_orientations_iterator(G):
8498
8599
A tree cannot be strongly oriented::
86100
87-
sage: g = graphs.RandomTree(100)
101+
sage: g = graphs.RandomTree(10)
88102
sage: len(list(g.strong_orientations_iterator()))
89103
0
90104
@@ -115,44 +129,37 @@ def strong_orientations_iterator(G):
115129
sage: g = graphs.PetersenGraph()
116130
sage: nr1 = len(list(g.strong_orientations_iterator()))
117131
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
119133
True
120-
121134
"""
122135
# if the graph has a bridge or is disconnected,
123136
# 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)):
125138
return
126139

127-
V = G.vertices(sort=False)
128-
Dg = DiGraph([V, G.edges(sort=False)], pos=G.get_pos())
140+
V = list(G)
129141

130142
# 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)
135150

136151
# initialization of the first binary word 00...0
137152
# corresponding to the current orientation of the non-tree edges
138153
existingAedges = [0] * len(A)
139154

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-
147155
# Generate all orientations for non-tree edges (using Gray code)
148156
# Each of these orientations can be extended to a strong orientation
149157
# of G by orienting properly the tree-edges
150158
previousWord = 0
151-
i = 0
152159

153160
# the orientation of one edge is fixed so we consider one edge less
154161
nr = 2**(len(A) - 1)
155-
while i < nr:
162+
for i in range(nr):
156163
word = (i >> 1) ^ i
157164
bitChanged = word ^ previousWord
158165

@@ -169,9 +176,7 @@ def strong_orientations_iterator(G):
169176
Dg.reverse_edge(A[bit][1], A[bit][0])
170177
existingAedges[bit] = 0
171178
# launch the algorithm for enumeration of the solutions
172-
for sol in _strong_orientations_of_a_mixed_graph(Dg, V, treeEdges):
173-
yield sol
174-
i = i + 1
179+
yield from _strong_orientations_of_a_mixed_graph(Dg, V, treeEdges)
175180

176181

177182
def _strong_orientations_of_a_mixed_graph(Dg, V, E):
@@ -201,9 +206,9 @@ def _strong_orientations_of_a_mixed_graph(Dg, V, E):
201206
202207
sage: from sage.graphs.orientations import _strong_orientations_of_a_mixed_graph
203208
sage: g = graphs.CycleGraph(5)
204-
sage: Dg = DiGraph(g) # all edges of g will be doubly oriented
209+
sage: Dg = DiGraph(g) # all edges of g will be doubly oriented
205210
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
211+
sage: len(list(it)) # there are two orientations of this multigraph
207212
2
208213
"""
209214
length = len(E)
@@ -213,7 +218,9 @@ def _strong_orientations_of_a_mixed_graph(Dg, V, E):
213218
u, v = E[i]
214219
Dg.delete_edge(u, v)
215220
if not (v in Dg.depth_first_search(u)):
216-
del E[i]
221+
# del E[i] in constant time
222+
E[i] = E[-1]
223+
E.pop()
217224
length -= 1
218225
Dg.add_edge(u, v)
219226
Dg.delete_edge(v, u)
@@ -222,7 +229,9 @@ def _strong_orientations_of_a_mixed_graph(Dg, V, E):
222229
Dg.add_edge(u, v)
223230
Dg.delete_edge(v, u)
224231
if not (u in Dg.depth_first_search(v)):
225-
del E[i]
232+
# del E[i] in constant time
233+
E[i] = E[-1]
234+
E.pop()
226235
length -= 1
227236
boundEdges.append((u, v))
228237
Dg.delete_edge(u, v)

0 commit comments

Comments
 (0)