Skip to content

Commit

Permalink
gh-38809: move orientation methods from graph.py to orientations.py
Browse files Browse the repository at this point in the history
    
We gather all orientation methods to file
`src/sage/graphs/orientations.py`.
This should ease the work of #38758.

### 📝 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.
- [ ] 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: #38809
Reported by: David Coudert
Reviewer(s): Travis Scrimshaw
  • Loading branch information
Release Manager committed Nov 2, 2024
2 parents 83363ef + f08cc2b commit 1f3ede8
Show file tree
Hide file tree
Showing 3 changed files with 702 additions and 628 deletions.
105 changes: 0 additions & 105 deletions src/sage/graphs/generic_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,6 @@
:widths: 30, 70
:delim: |

:meth:`~GenericGraph.eulerian_orientation` | Return a DiGraph which is an Eulerian orientation of the current graph.
:meth:`~GenericGraph.eulerian_circuit` | Return a list of edges forming an Eulerian circuit if one exists.
:meth:`~GenericGraph.minimum_cycle_basis` | Return a minimum weight cycle basis of the graph.
:meth:`~GenericGraph.cycle_basis` | Return a list of cycles which form a basis of the cycle space of ``self``.
Expand Down Expand Up @@ -4732,110 +4731,6 @@ def size(self):

num_edges = size

# Orientations

def eulerian_orientation(self):
r"""
Return a DiGraph which is an Eulerian orientation of the current graph.

An Eulerian graph being a graph such that any vertex has an even degree,
an Eulerian orientation of a graph is an orientation of its edges such
that each vertex `v` verifies `d^+(v)=d^-(v)=d(v)/2`, where `d^+` and
`d^-` respectively represent the out-degree and the in-degree of a
vertex.

If the graph is not Eulerian, the orientation verifies for any vertex
`v` that `| d^+(v)-d^-(v) | \leq 1`.

ALGORITHM:

This algorithm is a random walk through the edges of the graph, which
orients the edges according to the walk. When a vertex is reached which
has no non-oriented edge (this vertex must have odd degree), the walk
resumes at another vertex of odd degree, if any.

This algorithm has complexity `O(n+m)` for ``SparseGraph`` and `O(n^2)`
for ``DenseGraph``, where `m` is the number of edges in the graph and
`n` is the number of vertices in the graph.

EXAMPLES:

The CubeGraph with parameter 4, which is regular of even degree, has an
Eulerian orientation such that `d^+ = d^-`::

sage: g = graphs.CubeGraph(4)
sage: g.degree()
[4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4]
sage: o = g.eulerian_orientation()
sage: o.in_degree()
[2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
sage: o.out_degree()
[2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]

Secondly, the Petersen Graph, which is 3 regular has an orientation such
that the difference between `d^+` and `d^-` is at most 1::

sage: g = graphs.PetersenGraph()
sage: o = g.eulerian_orientation()
sage: o.in_degree()
[2, 2, 2, 2, 2, 1, 1, 1, 1, 1]
sage: o.out_degree()
[1, 1, 1, 1, 1, 2, 2, 2, 2, 2]

TESTS::

sage: E0 = Graph(); E4 = Graph(4) # See trac #21741
sage: E0.eulerian_orientation()
Digraph on 0 vertices
sage: E4.eulerian_orientation()
Digraph on 4 vertices
"""
from sage.graphs.digraph import DiGraph

d = DiGraph()
d.add_vertices(self.vertex_iterator())

if not self.size():
return d

g = copy(self)

# list of vertices of odd degree
odd = [x for x in g.vertex_iterator() if g.degree(x) % 2]

# Picks the first vertex, which is preferably an odd one
if odd:
v = odd.pop()
else:
v = next(g.edge_iterator(labels=None))[0]
odd.append(v)
# Stops when there is no edge left
while True:

# If there is an edge adjacent to the current one
if g.degree(v):
e = next(g.edge_iterator(v))
g.delete_edge(e)
if e[0] != v:
e = (e[1], e[0], e[2])
d.add_edge(e)
v = e[1]

# The current vertex is isolated
else:
odd.remove(v)

# jumps to another odd vertex if possible
if odd:
v = odd.pop()
# Else jumps to an even vertex which is not isolated
elif g.size():
v = next(g.edge_iterator())[0]
odd.append(v)
# If there is none, we are done !
else:
return d

def eulerian_circuit(self, return_vertices=False, labels=True, path=False):
r"""
Return a list of edges forming an Eulerian circuit if one exists.
Expand Down
Loading

0 comments on commit 1f3ede8

Please sign in to comment.