diff --git a/pattern/graph/__init__.py b/pattern/graph/__init__.py index 471f39f0..6d1870e4 100644 --- a/pattern/graph/__init__.py +++ b/pattern/graph/__init__.py @@ -232,9 +232,9 @@ def _set_weight(self, v): self._weight = v # Clear cached adjacency map in the graph, since edge weights have changed. if self.node1.graph is not None: - self.node1.graph._adjacency = self.node1.graph._paths = None + self.node1.graph._adjacency = None if self.node2.graph is not None: - self.node2.graph._adjacency = self.node2.graph._paths = None + self.node2.graph._adjacency = None weight = property(_get_weight, _set_weight) @@ -311,7 +311,6 @@ def __init__(self, layout=SPRING, distance=10.0): self.edges = [] # List of Edge objects. self.root = None self._adjacency = None # Cached adjacency() dict. - self._paths = None # Cached shortest paths dict. self.layout = layout==SPRING and GraphSpringLayout(self) or GraphLayout(self) self.distance = distance @@ -341,7 +340,7 @@ def add_node(self, id, *args, **kwargs): self[n.id] = n; n.graph = self self.root = kwargs.get("root", False) and n or self.root # Clear adjacency cache. - self._adjacency = self._paths = None + self._adjacency = None return n def add_edge(self, id1, id2, *args, **kwargs): @@ -366,7 +365,7 @@ def add_edge(self, id1, id2, *args, **kwargs): n1.links.append(n2, edge=e2) n2.links.append(n1, edge=e1 or e2) # Clear adjacency cache. - self._adjacency = self._paths = None + self._adjacency = None return e2 def remove(self, x): @@ -385,7 +384,7 @@ def remove(self, x): if isinstance(x, Edge): self.edges.remove(x) # Clear adjacency cache. - self._adjacency = self._paths = None + self._adjacency = None def node(self, id): """ Returns the node in the graph with the given id. @@ -413,21 +412,9 @@ def shortest_path(self, node1, node2, heuristic=None, directed=False): node1 = self[node1] if not isinstance(node2, Node): node2 = self[node2] - # Cache reversed path if directed=False. - id1 = (node1.id, node2.id, heuristic, directed) - id2 = (node2.id, node1.id, heuristic, directed) - if self._paths is not None and id1 in self._paths: - return self._paths[id1] try: p = dijkstra_shortest_path(self, node1.id, node2.id, heuristic, directed) p = [self[id] for id in p] - if self._paths is None or len(self._paths) > 1000: - self._paths = {} - if directed is True: - self._paths[id1] = p - if directed is False: - self._paths[id1] = p - self._paths[id2] = list(reversed(p)) return p except IndexError: return None @@ -749,8 +736,11 @@ def adjacency(graph, directed=False, reversed=False, stochastic=False, heuristic A heuristic function can be given that takes two node id's and returns an additional cost for movement between the two nodes. """ + # Note: caching a heuristic that is a method won't work. + # Bound method objects are transient, + # i.e., id(object.method) returns a new value each time. if graph._adjacency is not None and \ - graph._adjacency[1:] == (directed, reversed, stochastic, heuristic): + graph._adjacency[1:] == (directed, reversed, stochastic, heuristic and id(heuristic)): return graph._adjacency[0] map = {} for n in graph.nodes: @@ -768,7 +758,7 @@ def adjacency(graph, directed=False, reversed=False, stochastic=False, heuristic for id2 in map[id1]: map[id1][id2] /= n # Cache the adjacency map: this makes dijkstra_shortest_path() 2x faster in repeated use. - graph._adjacency = (map, directed, reversed, stochastic, heuristic) + graph._adjacency = (map, directed, reversed, stochastic, heuristic and id(heuristic)) return map def dijkstra_shortest_path(graph, id1, id2, heuristic=None, directed=False): @@ -893,30 +883,30 @@ def brandes_betweenness_centrality(graph, normalized=True, directed=False): heappush(Q, (0, id, id)) S = [] E = dict.fromkeys(graph, 0) # sigma - E[id] = 1 + E[id] = 1.0 while Q: (dist, pred, v) = heappop(Q) - if v in D: continue + if v in D: + continue D[v] = dist S.append(v) - E[v] = E[v] + E[pred] - for w in W[v].iterkeys(): + E[v] += E[pred] + for w in W[v]: vw_dist = D[v] + W[v][w] if w not in D and (w not in seen or vw_dist < seen[w]): seen[w] = vw_dist heappush(Q, (vw_dist, v, w)) P[w] = [v] - E[w] = 0 - if vw_dist == seen[w]: # Handle equal paths. + E[w] = 0.0 + elif vw_dist == seen[w]: # Handle equal paths. P[w].append(v) - E[w] = E[w] + E[v] - d = dict.fromkeys(graph, 0) - while S: - w = S.pop() - for v in P[w]: - d[v] = d[v] + (float(E[v]) / float(E[w])) * (1.0 + d[w]) + E[w] += E[v] + d = dict.fromkeys(graph, 0.0) + for w in S: + for v in P[w]: + d[v] += (1.0 + d[w]) * E[v] / E[w] if w != id: - b[w] = b[w] + d[w] + b[w] += d[w] # Normalize between 0.0 and 1.0. m = normalized and max(b.values()) or 1 b = dict((id, w/m) for id, w in b.iteritems())