Skip to content

Commit

Permalink
Trac #19077: Greatly speed up equality check of equal graphs
Browse files Browse the repository at this point in the history
The current code goes over all vertex pairs and checks existence of all
possible edges, which is extremely inefficient for, say, posets, whose
graphs are far from complete. This is a big problem when mixed with
unique representation of finite posets.

So let's iterate over edges directly.

URL: http://trac.sagemath.org/19077
Reported by: novoselt
Ticket author(s): Andrey Novoseltsev
Reviewer(s): Jori Mäntysalo, Nathann Cohen
  • Loading branch information
Release Manager authored and vbraun committed Aug 26, 2015
2 parents 67495d1 + 959c85d commit fbbaf32
Showing 1 changed file with 36 additions and 38 deletions.
74 changes: 36 additions & 38 deletions src/sage/graphs/generic_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -457,52 +457,50 @@ def __eq__(self, other):
"""
# inputs must be (di)graphs:
if not isinstance(other, GenericGraph):
raise TypeError("cannot compare graph to non-graph (%s)"%str(other))
return False
from sage.graphs.all import Graph
g1_is_graph = isinstance(self, Graph) # otherwise, DiGraph
g2_is_graph = isinstance(other, Graph) # otherwise, DiGraph

if (g1_is_graph != g2_is_graph):
return False
if self.allows_multiple_edges() != other.allows_multiple_edges():
return False
if self.allows_loops() != other.allows_loops():
return False
if self.order() != other.order():
return False
if self.size() != other.size():
return False
# Fast checks
if (g1_is_graph != g2_is_graph or
self.allows_multiple_edges() != other.allows_multiple_edges() or
self.allows_loops() != other.allows_loops() or
self.order() != other.order() or
self.size() != other.size() or
self.weighted() != other.weighted()):
return False
# Vertices
if any(x not in other for x in self):
return False
if self.weighted() != other.weighted():
return False
verts = self.vertices()
# Finally, we are prepared to check edges:
if not self.allows_multiple_edges():
for i in verts:
for j in verts:
if self.has_edge(i,j) != other.has_edge(i,j):
return False
if self.has_edge(i,j) and self._weighted and other._weighted:
if self.edge_label(i,j) != other.edge_label(i,j):
return False
return True
else:
for i in verts:
for j in verts:
if self.has_edge(i, j):
edges1 = self.edge_label(i, j)
else:
edges1 = []
if other.has_edge(i, j):
edges2 = other.edge_label(i, j)
else:
edges2 = []
if len(edges1) != len(edges2):
return False
if sorted(edges1) != sorted(edges2) and self._weighted and other._weighted:
return all(other.has_edge(*edge)
for edge in self.edge_iterator(labels=self._weighted))
# The problem with multiple edges is that labels may not have total
# ordering, which makes it difficult to compare lists of labels.
last_i = last_j = None
for i, j in self.edge_iterator(labels=False):
if i == last_i and j == last_j:
continue
last_i, last_j = i, j
# All labels between i and j
labels1 = self.edge_label(i, j)
try:
labels2 = other.edge_label(i, j)
except LookupError:
return False
if len(labels1) != len(labels2):
return False
if self._weighted:
# If there is total ordering, sorting will speed up things
labels1.sort()
labels2.sort()
for l in labels1:
try:
labels2.remove(l)
except ValueError:
return False
return True
return True

@cached_method
def __hash__(self):
Expand Down

0 comments on commit fbbaf32

Please sign in to comment.