Skip to content

Commit 57e2983

Browse files
committed
Rework Subgraph and Adjacency
This fixes subgraph/subadjacency/subnbrdict to - contain only nodes in the subgraph - protect nbrdicts as readonly, but let datadict be read-write - cache results for performance For both adjacency and subadjacency: - adjacency yields read-only nbrdicts - nbrdict yields read-write datadicts Renamed SubNodeData to SubNbrDict to avoid confusion with NodeData fixed G.size in graph.py
1 parent bcacfb9 commit 57e2983

File tree

3 files changed

+120
-40
lines changed

3 files changed

+120
-40
lines changed

adjacency.py

Lines changed: 56 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,62 @@
11
from collections import MappingView
2-
import networkx as nx
32

4-
class Adjacency(MappingView):
5-
def __getitem__(self, key):
6-
return self._mapping[key]
7-
def __iter__(self):
8-
for n,nbrs in self._mapping.items():
9-
yield n,nbrs
3+
class NbrDict(MappingView):
4+
__slots__ = ["_mapping"]
5+
# We can return _mapping[n] without wrapper. datadict is read-write
106
def __repr__(self):
11-
return '{0.__class__.__name__}({1})'.format(self,list(self._mapping.items()))
7+
return '{0.__class__.__name__}({0._mapping})'.format(self)
8+
def __iter__(self):
9+
return iter(self._mapping)
10+
def __getitem__(self, n):
11+
return self._mapping[n]
12+
def __contains__(self, node):
13+
return node in self._mapping
14+
def __len__(self):
15+
return len(self._mapping)
16+
17+
def keys(self):
18+
return self._mapping.keys()
19+
def data(self):
20+
return self._mapping.values() # Not readonly datadict
21+
def items(self):
22+
return self._mapping.items() # Not readonly datadict
23+
def __eq__(self, other):
24+
return dict(self.items()) == dict(other.items())
25+
def __ne__(self, other):
26+
return not self.__eq__(other)
27+
28+
29+
class Adjacency(NbrDict):
30+
# __slots__= ["_mapping","_cache"]
31+
def __init__(self, mapping):
32+
self._mapping = mapping
33+
self._cache = {}
34+
def __iter__(self):
35+
for n in self._mapping:
36+
if n in self._cache:
37+
yield (n, self._cache[n])
38+
else:
39+
# NbrDicts are read-only so use wrapper for mapping[n]
40+
self._cache[n] = nbrdict = NbrDict(self._mapping[n])
41+
yield n,nbrdict
42+
def __getitem__(self, n):
43+
if n in self._cache:
44+
return self._cache[n]
45+
if n in self._mapping:
46+
# NbrDicts are read-only so use wrapper for mapping[n]
47+
self._cache[n] = nbrdict = NbrDict(self._mapping[n])
48+
return nbrdict
49+
raise KeyError
50+
def data(self):
51+
# In Python3 we can remove the "set"
52+
for n in set(self._mapping.keys()) - set(self._cache.keys()):
53+
# NbrDicts are read-only so use wrapper for mapping[n]
54+
self.cache[n] = NbrDict(self.mapping[n])
55+
return self._cache.values()
56+
def items(self):
57+
for n in set(self._mapping.keys()) - set(self._cache.keys()):
58+
self.cache[n] = NbrDict(self.mapping[n])
59+
return self._cache.items() # Not readonly datadict
1260

1361
def list(self, nodelist=None):
1462
if nodelist is None:

graph.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ def order(self):
8484
return len(self.n)
8585

8686
def size(self, weight=None):
87-
s = sum(d for v, d in self.degree(weight=weight))
87+
s = sum(d for v, d in self.n.degree(weight=weight))
8888
# If `weight` is None, the sum of the degrees is guaranteed to be
8989
# even, so we can perform integer division and hence return an
9090
# integer. Otherwise, the sum of the weighted degrees is not

subgraph.py

Lines changed: 63 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from collections import MappingView,KeysView,ValuesView,ItemsView
2+
23
from nodes import Nodes
34
from edges import Edges
45
from adjacency import Adjacency
@@ -7,16 +8,22 @@
78
class Subgraph(object):
89
# __slots__ = ('_nodedata','_adjacency','data')
910
def __init__(self, graph, subnodes):
11+
# TODO Can we replace nbunch_iter with set(subnodes) & set(graph)?
12+
# We lose the Error messages...
1013
self._subnodes = set(self._nbunch_iter(graph, subnodes))
11-
self.data = graph.data
12-
self._nodedata = SubNodeData(self._subnodes, graph._nodedata)
14+
self._nodedata = SubNbrDict(self._subnodes, graph._nodedata)
1315
self._adjacency = SubAdjacency(self._subnodes, graph._adjacency)
14-
self.test=self._adjacency
16+
self.data = graph.data
1517
self.n = Nodes(self._nodedata, self._adjacency)
1618
self.e = Edges(self._nodedata, self._adjacency)
17-
self.a = Adjacency(self._adjacency)
19+
self.a = self._adjacency
1820
def __repr__(self):
1921
return '{0.__class__.__name__}({1})'.format(self,list(self._subnodes))
22+
def __iter__(self):
23+
return iter(self._subnodes)
24+
def __len__(self):
25+
return len(self._subnodes)
26+
2027
@staticmethod
2128
def _nbunch_iter(graph, nbunch=None):
2229
if nbunch is None: # include all nodes via iterator
@@ -46,41 +53,66 @@ def bunch_iter(nlist, adj):
4653

4754

4855

49-
class SubNodeData(MappingView):
56+
class SubNbrDict(MappingView):
57+
# __slots__= ["_nodes","_mapping","_cache"]
5058
def __init__(self, nodes, mapping):
51-
self._nodes = nodes
59+
# In nodes to be in subgraph, in mapping to be in nbrs.
60+
# So need intersection of nodes with mapping...
61+
self._nodes = set(nodes) & set(mapping)
5262
self._mapping = mapping
53-
def __iter__(self):
54-
for n in self._nodes:
55-
yield n
56-
def __getitem__(self, key):
57-
if key in self._nodes:
58-
return self._mapping[key]
59-
else:
60-
raise KeyError
61-
def __contains__(self, key):
62-
return key in self._nodes
63+
self._cache = {}
6364
def __repr__(self):
64-
return '{}'.format(self._nodes)
65+
return '{0.__class__.__name__}({0._nodes}, {0._mapping})'.format(self)
66+
def __iter__(self):
67+
return iter(self._nodes)
68+
def __getitem__(self, n):
69+
if n in self._nodes:
70+
# Datadicts are read/write so no wrapper for mapping[n]
71+
return self._mapping[n]
72+
raise KeyError
73+
def __contains__(self, n):
74+
return n in self._nodes
6575
def __len__(self):
6676
return len(self._nodes)
77+
6778
def keys(self):
68-
return KeysView(self._nodes)
79+
return self._nodes.keys()
80+
def data(self):
81+
# Datadicts are read/write so no wrapper for mapping[n]
82+
for n in self._nodes - set(self._cache.keys()):
83+
self._cache[n] = self._mapping[n]
84+
return self._cache.values()
6985
def items(self):
70-
return dict((k,v) for (k,v) in self._mapping.items() if k in self._nodes
71-
).items()
86+
# Datadicts are read/write so no wrapper for mapping[n]
87+
for n in self._nodes - set(self._cache.keys()):
88+
self._cache[n] = self._mapping[n]
89+
return self._cache.items()
7290

73-
class SubAdjacency(SubNodeData):
91+
class SubAdjacency(SubNbrDict):
92+
#__slots__ = ["_nodes","_mapping","_cache"]
7493
def __iter__(self):
7594
for n in self._nodes:
76-
yield (n, SubNodeData(self._nodes, self._mapping[n]).items())
77-
def __getitem__(self, key):
78-
if key in self._nodes:
79-
return SubNodeData(self._nodes, self._mapping[key])
80-
else:
81-
raise KeyError
82-
def __repr__(self):
83-
return '{}'.format(self.items())
95+
if n in self._cache:
96+
yield (n, self._cache[n])
97+
else:
98+
# NbrDicts are read-only so use wrapper for mapping[n]
99+
self._cache[n] = nd = SubNbrDict(self._nodes, self._mapping[n])
100+
yield (n, nd)
101+
def __getitem__(self, n):
102+
if n in self._cache:
103+
return self._cache[n]
104+
if n in self._nodes:
105+
# NbrDicts are read-only so use wrapper for mapping[n]
106+
self._cache[n] = nd = SubNbrDict(self._nodes, self._mapping[n])
107+
return nd
108+
raise KeyError
109+
def data(self):
110+
# NbrDicts are read-only so use wrapper for mapping[n]
111+
for n in self._nodes - set(self._cache.keys()):
112+
self._cache[n] = SubNbrDict(self._nodes, self._mapping[n])
113+
return self._cache.values() # Not readonly datadict
84114
def items(self):
85-
for n in self._nodes:
86-
yield (n, dict(SubNodeData(self._nodes, self._mapping[n]).items()))
115+
# NbrDicts are read-only so use wrapper for mapping[n]
116+
for n in self._nodes - set(self._cache.keys()):
117+
self._cache[n] = SubNbrDict(self._nodes, self._mapping[n])
118+
return self._cache.items()

0 commit comments

Comments
 (0)