blob: 4e834c79b638fd93b576228203fde181496be0f0 [file] [log] [blame]
# -*- coding: utf-8 -*-
"""
Eulerian circuits and graphs.
"""
import networkx as nx
__author__ = """\n""".join(['Nima Mohammadi (nima.irt[AT]gmail.com)',
'Aric Hagberg <hagberg@lanl.gov>'])
# Copyright (C) 2010 by
# Aric Hagberg <hagberg@lanl.gov>
# Dan Schult <dschult@colgate.edu>
# Pieter Swart <swart@lanl.gov>
# All rights reserved.
# BSD license.
__all__ = ['is_eulerian', 'eulerian_circuit']
def is_eulerian(G):
"""Return True if G is an Eulerian graph, False otherwise.
An Eulerian graph is a graph with an Eulerian circuit.
Parameters
----------
G : graph
A NetworkX Graph
Examples
--------
>>> nx.is_eulerian(nx.DiGraph({0:[3], 1:[2], 2:[3], 3:[0, 1]}))
True
>>> nx.is_eulerian(nx.complete_graph(5))
True
>>> nx.is_eulerian(nx.petersen_graph())
False
Notes
-----
This implementation requires the graph to be connected
(or strongly connected for directed graphs).
"""
if G.is_directed():
# Every node must have equal in degree and out degree
for n in G.nodes_iter():
if G.in_degree(n) != G.out_degree(n):
return False
# Must be strongly connected
if not nx.is_strongly_connected(G):
return False
else:
# An undirected Eulerian graph has no vertices of odd degrees
for v,d in G.degree_iter():
if d % 2 != 0:
return False
# Must be connected
if not nx.is_connected(G):
return False
return True
def eulerian_circuit(G, source=None):
"""Return the edges of an Eulerian circuit in G.
An Eulerian circuit is a path that crosses every edge in G exactly once
and finishes at the starting node.
Parameters
----------
G : graph
A NetworkX Graph
source : node, optional
Starting node for circuit.
Returns
-------
edges : generator
A generator that produces edges in the Eulerian circuit.
Raises
------
NetworkXError
If the graph is not Eulerian.
See Also
--------
is_eulerian
Notes
-----
Uses Fleury's algorithm [1]_,[2]_
References
----------
.. [1] Fleury, "Deux problemes de geometrie de situation",
Journal de mathematiques elementaires (1883), 257-261.
.. [2] http://en.wikipedia.org/wiki/Eulerian_path
Examples
--------
>>> G=nx.complete_graph(3)
>>> list(nx.eulerian_circuit(G))
[(0, 1), (1, 2), (2, 0)]
>>> list(nx.eulerian_circuit(G,source=1))
[(1, 0), (0, 2), (2, 1)]
>>> [u for u,v in nx.eulerian_circuit(G)] # nodes in circuit
[0, 1, 2]
"""
if not is_eulerian(G):
raise nx.NetworkXError("G is not Eulerian.")
g = G.__class__(G) # copy graph structure (not attributes)
# set starting node
if source is None:
v = next(g.nodes_iter())
else:
v = source
while g.size() > 0:
n = v
# sort nbrs here to provide stable ordering of alternate cycles
nbrs = sorted([v for u,v in g.edges(n)])
for v in nbrs:
g.remove_edge(n,v)
bridge = not nx.is_connected(g.to_undirected())
if bridge:
g.add_edge(n,v) # add this edge back and try another
else:
break # this edge is good, break the for loop
if bridge:
g.remove_edge(n,v)
g.remove_node(n)
yield (n,v)