reportviews.py 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447
  1. """
  2. View Classes provide node, edge and degree "views" of a graph.
  3. Views for nodes, edges and degree are provided for all base graph classes.
  4. A view means a read-only object that is quick to create, automatically
  5. updated when the graph changes, and provides basic access like `n in V`,
  6. `for n in V`, `V[n]` and sometimes set operations.
  7. The views are read-only iterable containers that are updated as the
  8. graph is updated. As with dicts, the graph should not be updated
  9. while iterating through the view. Views can be iterated multiple times.
  10. Edge and Node views also allow data attribute lookup.
  11. The resulting attribute dict is writable as `G.edges[3, 4]['color']='red'`
  12. Degree views allow lookup of degree values for single nodes.
  13. Weighted degree is supported with the `weight` argument.
  14. NodeView
  15. ========
  16. `V = G.nodes` (or `V = G.nodes()`) allows `len(V)`, `n in V`, set
  17. operations e.g. "G.nodes & H.nodes", and `dd = G.nodes[n]`, where
  18. `dd` is the node data dict. Iteration is over the nodes by default.
  19. NodeDataView
  20. ============
  21. To iterate over (node, data) pairs, use arguments to `G.nodes()`
  22. to create a DataView e.g. `DV = G.nodes(data='color', default='red')`.
  23. The DataView iterates as `for n, color in DV` and allows
  24. `(n, 'red') in DV`. Using `DV = G.nodes(data=True)`, the DataViews
  25. use the full datadict in writeable form also allowing contain testing as
  26. `(n, {'color': 'red'}) in VD`. DataViews allow set operations when
  27. data attributes are hashable.
  28. DegreeView
  29. ==========
  30. `V = G.degree` allows iteration over (node, degree) pairs as well
  31. as lookup: `deg=V[n]`. There are many flavors of DegreeView
  32. for In/Out/Directed/Multi. For Directed Graphs, `G.degree`
  33. counts both in and out going edges. `G.out_degree` and
  34. `G.in_degree` count only specific directions.
  35. Weighted degree using edge data attributes is provide via
  36. `V = G.degree(weight='attr_name')` where any string with the
  37. attribute name can be used. `weight=None` is the default.
  38. No set operations are implemented for degrees, use NodeView.
  39. The argument `nbunch` restricts iteration to nodes in nbunch.
  40. The DegreeView can still lookup any node even if nbunch is specified.
  41. EdgeView
  42. ========
  43. `V = G.edges` or `V = G.edges()` allows iteration over edges as well as
  44. `e in V`, set operations and edge data lookup `dd = G.edges[2, 3]`.
  45. Iteration is over 2-tuples `(u, v)` for Graph/DiGraph. For multigraphs
  46. edges 3-tuples `(u, v, key)` are the default but 2-tuples can be obtained
  47. via `V = G.edges(keys=False)`.
  48. Set operations for directed graphs treat the edges as a set of 2-tuples.
  49. For undirected graphs, 2-tuples are not a unique representation of edges.
  50. So long as the set being compared to contains unique representations
  51. of its edges, the set operations will act as expected. If the other
  52. set contains both `(0, 1)` and `(1, 0)` however, the result of set
  53. operations may contain both representations of the same edge.
  54. EdgeDataView
  55. ============
  56. Edge data can be reported using an EdgeDataView typically created
  57. by calling an EdgeView: `DV = G.edges(data='weight', default=1)`.
  58. The EdgeDataView allows iteration over edge tuples, membership checking
  59. but no set operations.
  60. Iteration depends on `data` and `default` and for multigraph `keys`
  61. If `data is False` (the default) then iterate over 2-tuples `(u, v)`.
  62. If `data is True` iterate over 3-tuples `(u, v, datadict)`.
  63. Otherwise iterate over `(u, v, datadict.get(data, default))`.
  64. For Multigraphs, if `keys is True`, replace `u, v` with `u, v, key`
  65. to create 3-tuples and 4-tuples.
  66. The argument `nbunch` restricts edges to those incident to nodes in nbunch.
  67. """
  68. from abc import ABC
  69. from collections.abc import Mapping, Set
  70. import networkx as nx
  71. __all__ = [
  72. "NodeView",
  73. "NodeDataView",
  74. "EdgeView",
  75. "OutEdgeView",
  76. "InEdgeView",
  77. "EdgeDataView",
  78. "OutEdgeDataView",
  79. "InEdgeDataView",
  80. "MultiEdgeView",
  81. "OutMultiEdgeView",
  82. "InMultiEdgeView",
  83. "MultiEdgeDataView",
  84. "OutMultiEdgeDataView",
  85. "InMultiEdgeDataView",
  86. "DegreeView",
  87. "DiDegreeView",
  88. "InDegreeView",
  89. "OutDegreeView",
  90. "MultiDegreeView",
  91. "DiMultiDegreeView",
  92. "InMultiDegreeView",
  93. "OutMultiDegreeView",
  94. ]
  95. # NodeViews
  96. class NodeView(Mapping, Set):
  97. """A NodeView class to act as G.nodes for a NetworkX Graph
  98. Set operations act on the nodes without considering data.
  99. Iteration is over nodes. Node data can be looked up like a dict.
  100. Use NodeDataView to iterate over node data or to specify a data
  101. attribute for lookup. NodeDataView is created by calling the NodeView.
  102. Parameters
  103. ----------
  104. graph : NetworkX graph-like class
  105. Examples
  106. --------
  107. >>> G = nx.path_graph(3)
  108. >>> NV = G.nodes()
  109. >>> 2 in NV
  110. True
  111. >>> for n in NV:
  112. ... print(n)
  113. 0
  114. 1
  115. 2
  116. >>> assert NV & {1, 2, 3} == {1, 2}
  117. >>> G.add_node(2, color="blue")
  118. >>> NV[2]
  119. {'color': 'blue'}
  120. >>> G.add_node(8, color="red")
  121. >>> NDV = G.nodes(data=True)
  122. >>> (2, NV[2]) in NDV
  123. True
  124. >>> for n, dd in NDV:
  125. ... print((n, dd.get("color", "aqua")))
  126. (0, 'aqua')
  127. (1, 'aqua')
  128. (2, 'blue')
  129. (8, 'red')
  130. >>> NDV[2] == NV[2]
  131. True
  132. >>> NVdata = G.nodes(data="color", default="aqua")
  133. >>> (2, NVdata[2]) in NVdata
  134. True
  135. >>> for n, dd in NVdata:
  136. ... print((n, dd))
  137. (0, 'aqua')
  138. (1, 'aqua')
  139. (2, 'blue')
  140. (8, 'red')
  141. >>> NVdata[2] == NV[2] # NVdata gets 'color', NV gets datadict
  142. False
  143. """
  144. __slots__ = ("_nodes",)
  145. def __getstate__(self):
  146. return {"_nodes": self._nodes}
  147. def __setstate__(self, state):
  148. self._nodes = state["_nodes"]
  149. def __init__(self, graph):
  150. self._nodes = graph._node
  151. # Mapping methods
  152. def __len__(self):
  153. return len(self._nodes)
  154. def __iter__(self):
  155. return iter(self._nodes)
  156. def __getitem__(self, n):
  157. if isinstance(n, slice):
  158. raise nx.NetworkXError(
  159. f"{type(self).__name__} does not support slicing, "
  160. f"try list(G.nodes)[{n.start}:{n.stop}:{n.step}]"
  161. )
  162. return self._nodes[n]
  163. # Set methods
  164. def __contains__(self, n):
  165. return n in self._nodes
  166. @classmethod
  167. def _from_iterable(cls, it):
  168. return set(it)
  169. # DataView method
  170. def __call__(self, data=False, default=None):
  171. if data is False:
  172. return self
  173. return NodeDataView(self._nodes, data, default)
  174. def data(self, data=True, default=None):
  175. """
  176. Return a read-only view of node data.
  177. Parameters
  178. ----------
  179. data : bool or node data key, default=True
  180. If ``data=True`` (the default), return a `NodeDataView` object that
  181. maps each node to *all* of its attributes. `data` may also be an
  182. arbitrary key, in which case the `NodeDataView` maps each node to
  183. the value for the keyed attribute. In this case, if a node does
  184. not have the `data` attribute, the `default` value is used.
  185. default : object, default=None
  186. The value used when a node does not have a specific attribute.
  187. Returns
  188. -------
  189. NodeDataView
  190. The layout of the returned NodeDataView depends on the value of the
  191. `data` parameter.
  192. Notes
  193. -----
  194. If ``data=False``, returns a `NodeView` object without data.
  195. See Also
  196. --------
  197. NodeDataView
  198. Examples
  199. --------
  200. >>> G = nx.Graph()
  201. >>> G.add_nodes_from(
  202. ... [
  203. ... (0, {"color": "red", "weight": 10}),
  204. ... (1, {"color": "blue"}),
  205. ... (2, {"color": "yellow", "weight": 2}),
  206. ... ]
  207. ... )
  208. Accessing node data with ``data=True`` (the default) returns a
  209. NodeDataView mapping each node to all of its attributes:
  210. >>> G.nodes.data()
  211. NodeDataView({0: {'color': 'red', 'weight': 10}, 1: {'color': 'blue'}, 2: {'color': 'yellow', 'weight': 2}})
  212. If `data` represents a key in the node attribute dict, a NodeDataView mapping
  213. the nodes to the value for that specific key is returned:
  214. >>> G.nodes.data("color")
  215. NodeDataView({0: 'red', 1: 'blue', 2: 'yellow'}, data='color')
  216. If a specific key is not found in an attribute dict, the value specified
  217. by `default` is returned:
  218. >>> G.nodes.data("weight", default=-999)
  219. NodeDataView({0: 10, 1: -999, 2: 2}, data='weight')
  220. Note that there is no check that the `data` key is in any of the
  221. node attribute dictionaries:
  222. >>> G.nodes.data("height")
  223. NodeDataView({0: None, 1: None, 2: None}, data='height')
  224. """
  225. if data is False:
  226. return self
  227. return NodeDataView(self._nodes, data, default)
  228. def __str__(self):
  229. return str(list(self))
  230. def __repr__(self):
  231. return f"{self.__class__.__name__}({tuple(self)})"
  232. class NodeDataView(Set):
  233. """A DataView class for nodes of a NetworkX Graph
  234. The main use for this class is to iterate through node-data pairs.
  235. The data can be the entire data-dictionary for each node, or it
  236. can be a specific attribute (with default) for each node.
  237. Set operations are enabled with NodeDataView, but don't work in
  238. cases where the data is not hashable. Use with caution.
  239. Typically, set operations on nodes use NodeView, not NodeDataView.
  240. That is, they use `G.nodes` instead of `G.nodes(data='foo')`.
  241. Parameters
  242. ==========
  243. graph : NetworkX graph-like class
  244. data : bool or string (default=False)
  245. default : object (default=None)
  246. """
  247. __slots__ = ("_nodes", "_data", "_default")
  248. def __getstate__(self):
  249. return {"_nodes": self._nodes, "_data": self._data, "_default": self._default}
  250. def __setstate__(self, state):
  251. self._nodes = state["_nodes"]
  252. self._data = state["_data"]
  253. self._default = state["_default"]
  254. def __init__(self, nodedict, data=False, default=None):
  255. self._nodes = nodedict
  256. self._data = data
  257. self._default = default
  258. @classmethod
  259. def _from_iterable(cls, it):
  260. try:
  261. return set(it)
  262. except TypeError as err:
  263. if "unhashable" in str(err):
  264. msg = " : Could be b/c data=True or your values are unhashable"
  265. raise TypeError(str(err) + msg) from err
  266. raise
  267. def __len__(self):
  268. return len(self._nodes)
  269. def __iter__(self):
  270. data = self._data
  271. if data is False:
  272. return iter(self._nodes)
  273. if data is True:
  274. return iter(self._nodes.items())
  275. return (
  276. (n, dd[data] if data in dd else self._default)
  277. for n, dd in self._nodes.items()
  278. )
  279. def __contains__(self, n):
  280. try:
  281. node_in = n in self._nodes
  282. except TypeError:
  283. n, d = n
  284. return n in self._nodes and self[n] == d
  285. if node_in is True:
  286. return node_in
  287. try:
  288. n, d = n
  289. except (TypeError, ValueError):
  290. return False
  291. return n in self._nodes and self[n] == d
  292. def __getitem__(self, n):
  293. if isinstance(n, slice):
  294. raise nx.NetworkXError(
  295. f"{type(self).__name__} does not support slicing, "
  296. f"try list(G.nodes.data())[{n.start}:{n.stop}:{n.step}]"
  297. )
  298. ddict = self._nodes[n]
  299. data = self._data
  300. if data is False or data is True:
  301. return ddict
  302. return ddict[data] if data in ddict else self._default
  303. def __str__(self):
  304. return str(list(self))
  305. def __repr__(self):
  306. name = self.__class__.__name__
  307. if self._data is False:
  308. return f"{name}({tuple(self)})"
  309. if self._data is True:
  310. return f"{name}({dict(self)})"
  311. return f"{name}({dict(self)}, data={self._data!r})"
  312. # DegreeViews
  313. class DiDegreeView:
  314. """A View class for degree of nodes in a NetworkX Graph
  315. The functionality is like dict.items() with (node, degree) pairs.
  316. Additional functionality includes read-only lookup of node degree,
  317. and calling with optional features nbunch (for only a subset of nodes)
  318. and weight (use edge weights to compute degree).
  319. Parameters
  320. ==========
  321. graph : NetworkX graph-like class
  322. nbunch : node, container of nodes, or None meaning all nodes (default=None)
  323. weight : bool or string (default=None)
  324. Notes
  325. -----
  326. DegreeView can still lookup any node even if nbunch is specified.
  327. Examples
  328. --------
  329. >>> G = nx.path_graph(3)
  330. >>> DV = G.degree()
  331. >>> assert DV[2] == 1
  332. >>> assert sum(deg for n, deg in DV) == 4
  333. >>> DVweight = G.degree(weight="span")
  334. >>> G.add_edge(1, 2, span=34)
  335. >>> DVweight[2]
  336. 34
  337. >>> DVweight[0] # default edge weight is 1
  338. 1
  339. >>> sum(span for n, span in DVweight) # sum weighted degrees
  340. 70
  341. >>> DVnbunch = G.degree(nbunch=(1, 2))
  342. >>> assert len(list(DVnbunch)) == 2 # iteration over nbunch only
  343. """
  344. def __init__(self, G, nbunch=None, weight=None):
  345. self._graph = G
  346. self._succ = G._succ if hasattr(G, "_succ") else G._adj
  347. self._pred = G._pred if hasattr(G, "_pred") else G._adj
  348. self._nodes = self._succ if nbunch is None else list(G.nbunch_iter(nbunch))
  349. self._weight = weight
  350. def __call__(self, nbunch=None, weight=None):
  351. if nbunch is None:
  352. if weight == self._weight:
  353. return self
  354. return self.__class__(self._graph, None, weight)
  355. try:
  356. if nbunch in self._nodes:
  357. if weight == self._weight:
  358. return self[nbunch]
  359. return self.__class__(self._graph, None, weight)[nbunch]
  360. except TypeError:
  361. pass
  362. return self.__class__(self._graph, nbunch, weight)
  363. def __getitem__(self, n):
  364. weight = self._weight
  365. succs = self._succ[n]
  366. preds = self._pred[n]
  367. if weight is None:
  368. return len(succs) + len(preds)
  369. return sum(dd.get(weight, 1) for dd in succs.values()) + sum(
  370. dd.get(weight, 1) for dd in preds.values()
  371. )
  372. def __iter__(self):
  373. weight = self._weight
  374. if weight is None:
  375. for n in self._nodes:
  376. succs = self._succ[n]
  377. preds = self._pred[n]
  378. yield (n, len(succs) + len(preds))
  379. else:
  380. for n in self._nodes:
  381. succs = self._succ[n]
  382. preds = self._pred[n]
  383. deg = sum(dd.get(weight, 1) for dd in succs.values()) + sum(
  384. dd.get(weight, 1) for dd in preds.values()
  385. )
  386. yield (n, deg)
  387. def __len__(self):
  388. return len(self._nodes)
  389. def __str__(self):
  390. return str(list(self))
  391. def __repr__(self):
  392. return f"{self.__class__.__name__}({dict(self)})"
  393. class DegreeView(DiDegreeView):
  394. """A DegreeView class to act as G.degree for a NetworkX Graph
  395. Typical usage focuses on iteration over `(node, degree)` pairs.
  396. The degree is by default the number of edges incident to the node.
  397. Optional argument `weight` enables weighted degree using the edge
  398. attribute named in the `weight` argument. Reporting and iteration
  399. can also be restricted to a subset of nodes using `nbunch`.
  400. Additional functionality include node lookup so that `G.degree[n]`
  401. reported the (possibly weighted) degree of node `n`. Calling the
  402. view creates a view with different arguments `nbunch` or `weight`.
  403. Parameters
  404. ==========
  405. graph : NetworkX graph-like class
  406. nbunch : node, container of nodes, or None meaning all nodes (default=None)
  407. weight : string or None (default=None)
  408. Notes
  409. -----
  410. DegreeView can still lookup any node even if nbunch is specified.
  411. Examples
  412. --------
  413. >>> G = nx.path_graph(3)
  414. >>> DV = G.degree()
  415. >>> assert DV[2] == 1
  416. >>> assert G.degree[2] == 1
  417. >>> assert sum(deg for n, deg in DV) == 4
  418. >>> DVweight = G.degree(weight="span")
  419. >>> G.add_edge(1, 2, span=34)
  420. >>> DVweight[2]
  421. 34
  422. >>> DVweight[0] # default edge weight is 1
  423. 1
  424. >>> sum(span for n, span in DVweight) # sum weighted degrees
  425. 70
  426. >>> DVnbunch = G.degree(nbunch=(1, 2))
  427. >>> assert len(list(DVnbunch)) == 2 # iteration over nbunch only
  428. """
  429. def __getitem__(self, n):
  430. weight = self._weight
  431. nbrs = self._succ[n]
  432. if weight is None:
  433. return len(nbrs) + (n in nbrs)
  434. return sum(dd.get(weight, 1) for dd in nbrs.values()) + (
  435. n in nbrs and nbrs[n].get(weight, 1)
  436. )
  437. def __iter__(self):
  438. weight = self._weight
  439. if weight is None:
  440. for n in self._nodes:
  441. nbrs = self._succ[n]
  442. yield (n, len(nbrs) + (n in nbrs))
  443. else:
  444. for n in self._nodes:
  445. nbrs = self._succ[n]
  446. deg = sum(dd.get(weight, 1) for dd in nbrs.values()) + (
  447. n in nbrs and nbrs[n].get(weight, 1)
  448. )
  449. yield (n, deg)
  450. class OutDegreeView(DiDegreeView):
  451. """A DegreeView class to report out_degree for a DiGraph; See DegreeView"""
  452. def __getitem__(self, n):
  453. weight = self._weight
  454. nbrs = self._succ[n]
  455. if self._weight is None:
  456. return len(nbrs)
  457. return sum(dd.get(self._weight, 1) for dd in nbrs.values())
  458. def __iter__(self):
  459. weight = self._weight
  460. if weight is None:
  461. for n in self._nodes:
  462. succs = self._succ[n]
  463. yield (n, len(succs))
  464. else:
  465. for n in self._nodes:
  466. succs = self._succ[n]
  467. deg = sum(dd.get(weight, 1) for dd in succs.values())
  468. yield (n, deg)
  469. class InDegreeView(DiDegreeView):
  470. """A DegreeView class to report in_degree for a DiGraph; See DegreeView"""
  471. def __getitem__(self, n):
  472. weight = self._weight
  473. nbrs = self._pred[n]
  474. if weight is None:
  475. return len(nbrs)
  476. return sum(dd.get(weight, 1) for dd in nbrs.values())
  477. def __iter__(self):
  478. weight = self._weight
  479. if weight is None:
  480. for n in self._nodes:
  481. preds = self._pred[n]
  482. yield (n, len(preds))
  483. else:
  484. for n in self._nodes:
  485. preds = self._pred[n]
  486. deg = sum(dd.get(weight, 1) for dd in preds.values())
  487. yield (n, deg)
  488. class MultiDegreeView(DiDegreeView):
  489. """A DegreeView class for undirected multigraphs; See DegreeView"""
  490. def __getitem__(self, n):
  491. weight = self._weight
  492. nbrs = self._succ[n]
  493. if weight is None:
  494. return sum(len(keys) for keys in nbrs.values()) + (
  495. n in nbrs and len(nbrs[n])
  496. )
  497. # edge weighted graph - degree is sum of nbr edge weights
  498. deg = sum(
  499. d.get(weight, 1) for key_dict in nbrs.values() for d in key_dict.values()
  500. )
  501. if n in nbrs:
  502. deg += sum(d.get(weight, 1) for d in nbrs[n].values())
  503. return deg
  504. def __iter__(self):
  505. weight = self._weight
  506. if weight is None:
  507. for n in self._nodes:
  508. nbrs = self._succ[n]
  509. deg = sum(len(keys) for keys in nbrs.values()) + (
  510. n in nbrs and len(nbrs[n])
  511. )
  512. yield (n, deg)
  513. else:
  514. for n in self._nodes:
  515. nbrs = self._succ[n]
  516. deg = sum(
  517. d.get(weight, 1)
  518. for key_dict in nbrs.values()
  519. for d in key_dict.values()
  520. )
  521. if n in nbrs:
  522. deg += sum(d.get(weight, 1) for d in nbrs[n].values())
  523. yield (n, deg)
  524. class DiMultiDegreeView(DiDegreeView):
  525. """A DegreeView class for MultiDiGraph; See DegreeView"""
  526. def __getitem__(self, n):
  527. weight = self._weight
  528. succs = self._succ[n]
  529. preds = self._pred[n]
  530. if weight is None:
  531. return sum(len(keys) for keys in succs.values()) + sum(
  532. len(keys) for keys in preds.values()
  533. )
  534. # edge weighted graph - degree is sum of nbr edge weights
  535. deg = sum(
  536. d.get(weight, 1) for key_dict in succs.values() for d in key_dict.values()
  537. ) + sum(
  538. d.get(weight, 1) for key_dict in preds.values() for d in key_dict.values()
  539. )
  540. return deg
  541. def __iter__(self):
  542. weight = self._weight
  543. if weight is None:
  544. for n in self._nodes:
  545. succs = self._succ[n]
  546. preds = self._pred[n]
  547. deg = sum(len(keys) for keys in succs.values()) + sum(
  548. len(keys) for keys in preds.values()
  549. )
  550. yield (n, deg)
  551. else:
  552. for n in self._nodes:
  553. succs = self._succ[n]
  554. preds = self._pred[n]
  555. deg = sum(
  556. d.get(weight, 1)
  557. for key_dict in succs.values()
  558. for d in key_dict.values()
  559. ) + sum(
  560. d.get(weight, 1)
  561. for key_dict in preds.values()
  562. for d in key_dict.values()
  563. )
  564. yield (n, deg)
  565. class InMultiDegreeView(DiDegreeView):
  566. """A DegreeView class for inward degree of MultiDiGraph; See DegreeView"""
  567. def __getitem__(self, n):
  568. weight = self._weight
  569. nbrs = self._pred[n]
  570. if weight is None:
  571. return sum(len(data) for data in nbrs.values())
  572. # edge weighted graph - degree is sum of nbr edge weights
  573. return sum(
  574. d.get(weight, 1) for key_dict in nbrs.values() for d in key_dict.values()
  575. )
  576. def __iter__(self):
  577. weight = self._weight
  578. if weight is None:
  579. for n in self._nodes:
  580. nbrs = self._pred[n]
  581. deg = sum(len(data) for data in nbrs.values())
  582. yield (n, deg)
  583. else:
  584. for n in self._nodes:
  585. nbrs = self._pred[n]
  586. deg = sum(
  587. d.get(weight, 1)
  588. for key_dict in nbrs.values()
  589. for d in key_dict.values()
  590. )
  591. yield (n, deg)
  592. class OutMultiDegreeView(DiDegreeView):
  593. """A DegreeView class for outward degree of MultiDiGraph; See DegreeView"""
  594. def __getitem__(self, n):
  595. weight = self._weight
  596. nbrs = self._succ[n]
  597. if weight is None:
  598. return sum(len(data) for data in nbrs.values())
  599. # edge weighted graph - degree is sum of nbr edge weights
  600. return sum(
  601. d.get(weight, 1) for key_dict in nbrs.values() for d in key_dict.values()
  602. )
  603. def __iter__(self):
  604. weight = self._weight
  605. if weight is None:
  606. for n in self._nodes:
  607. nbrs = self._succ[n]
  608. deg = sum(len(data) for data in nbrs.values())
  609. yield (n, deg)
  610. else:
  611. for n in self._nodes:
  612. nbrs = self._succ[n]
  613. deg = sum(
  614. d.get(weight, 1)
  615. for key_dict in nbrs.values()
  616. for d in key_dict.values()
  617. )
  618. yield (n, deg)
  619. # A base class for all edge views. Ensures all edge view and edge data view
  620. # objects/classes are captured by `isinstance(obj, EdgeViewABC)` and
  621. # `issubclass(cls, EdgeViewABC)` respectively
  622. class EdgeViewABC(ABC):
  623. pass
  624. # EdgeDataViews
  625. class OutEdgeDataView(EdgeViewABC):
  626. """EdgeDataView for outward edges of DiGraph; See EdgeDataView"""
  627. __slots__ = (
  628. "_viewer",
  629. "_nbunch",
  630. "_data",
  631. "_default",
  632. "_adjdict",
  633. "_nodes_nbrs",
  634. "_report",
  635. )
  636. def __getstate__(self):
  637. return {
  638. "viewer": self._viewer,
  639. "nbunch": self._nbunch,
  640. "data": self._data,
  641. "default": self._default,
  642. }
  643. def __setstate__(self, state):
  644. self.__init__(**state)
  645. def __init__(self, viewer, nbunch=None, data=False, *, default=None):
  646. self._viewer = viewer
  647. adjdict = self._adjdict = viewer._adjdict
  648. if nbunch is None:
  649. self._nodes_nbrs = adjdict.items
  650. else:
  651. # dict retains order of nodes but acts like a set
  652. nbunch = dict.fromkeys(viewer._graph.nbunch_iter(nbunch))
  653. self._nodes_nbrs = lambda: [(n, adjdict[n]) for n in nbunch]
  654. self._nbunch = nbunch
  655. self._data = data
  656. self._default = default
  657. # Set _report based on data and default
  658. if data is True:
  659. self._report = lambda n, nbr, dd: (n, nbr, dd)
  660. elif data is False:
  661. self._report = lambda n, nbr, dd: (n, nbr)
  662. else: # data is attribute name
  663. self._report = (
  664. lambda n, nbr, dd: (n, nbr, dd[data])
  665. if data in dd
  666. else (n, nbr, default)
  667. )
  668. def __len__(self):
  669. return sum(len(nbrs) for n, nbrs in self._nodes_nbrs())
  670. def __iter__(self):
  671. return (
  672. self._report(n, nbr, dd)
  673. for n, nbrs in self._nodes_nbrs()
  674. for nbr, dd in nbrs.items()
  675. )
  676. def __contains__(self, e):
  677. u, v = e[:2]
  678. if self._nbunch is not None and u not in self._nbunch:
  679. return False # this edge doesn't start in nbunch
  680. try:
  681. ddict = self._adjdict[u][v]
  682. except KeyError:
  683. return False
  684. return e == self._report(u, v, ddict)
  685. def __str__(self):
  686. return str(list(self))
  687. def __repr__(self):
  688. return f"{self.__class__.__name__}({list(self)})"
  689. class EdgeDataView(OutEdgeDataView):
  690. """A EdgeDataView class for edges of Graph
  691. This view is primarily used to iterate over the edges reporting
  692. edges as node-tuples with edge data optionally reported. The
  693. argument `nbunch` allows restriction to edges incident to nodes
  694. in that container/singleton. The default (nbunch=None)
  695. reports all edges. The arguments `data` and `default` control
  696. what edge data is reported. The default `data is False` reports
  697. only node-tuples for each edge. If `data is True` the entire edge
  698. data dict is returned. Otherwise `data` is assumed to hold the name
  699. of the edge attribute to report with default `default` if that
  700. edge attribute is not present.
  701. Parameters
  702. ----------
  703. nbunch : container of nodes, node or None (default None)
  704. data : False, True or string (default False)
  705. default : default value (default None)
  706. Examples
  707. --------
  708. >>> G = nx.path_graph(3)
  709. >>> G.add_edge(1, 2, foo="bar")
  710. >>> list(G.edges(data="foo", default="biz"))
  711. [(0, 1, 'biz'), (1, 2, 'bar')]
  712. >>> assert (0, 1, "biz") in G.edges(data="foo", default="biz")
  713. """
  714. __slots__ = ()
  715. def __len__(self):
  716. return sum(1 for e in self)
  717. def __iter__(self):
  718. seen = {}
  719. for n, nbrs in self._nodes_nbrs():
  720. for nbr, dd in nbrs.items():
  721. if nbr not in seen:
  722. yield self._report(n, nbr, dd)
  723. seen[n] = 1
  724. del seen
  725. def __contains__(self, e):
  726. u, v = e[:2]
  727. if self._nbunch is not None and u not in self._nbunch and v not in self._nbunch:
  728. return False # this edge doesn't start and it doesn't end in nbunch
  729. try:
  730. ddict = self._adjdict[u][v]
  731. except KeyError:
  732. return False
  733. return e == self._report(u, v, ddict)
  734. class InEdgeDataView(OutEdgeDataView):
  735. """An EdgeDataView class for outward edges of DiGraph; See EdgeDataView"""
  736. __slots__ = ()
  737. def __iter__(self):
  738. return (
  739. self._report(nbr, n, dd)
  740. for n, nbrs in self._nodes_nbrs()
  741. for nbr, dd in nbrs.items()
  742. )
  743. def __contains__(self, e):
  744. u, v = e[:2]
  745. if self._nbunch is not None and v not in self._nbunch:
  746. return False # this edge doesn't end in nbunch
  747. try:
  748. ddict = self._adjdict[v][u]
  749. except KeyError:
  750. return False
  751. return e == self._report(u, v, ddict)
  752. class OutMultiEdgeDataView(OutEdgeDataView):
  753. """An EdgeDataView for outward edges of MultiDiGraph; See EdgeDataView"""
  754. __slots__ = ("keys",)
  755. def __getstate__(self):
  756. return {
  757. "viewer": self._viewer,
  758. "nbunch": self._nbunch,
  759. "keys": self.keys,
  760. "data": self._data,
  761. "default": self._default,
  762. }
  763. def __setstate__(self, state):
  764. self.__init__(**state)
  765. def __init__(self, viewer, nbunch=None, data=False, *, default=None, keys=False):
  766. self._viewer = viewer
  767. adjdict = self._adjdict = viewer._adjdict
  768. self.keys = keys
  769. if nbunch is None:
  770. self._nodes_nbrs = adjdict.items
  771. else:
  772. # dict retains order of nodes but acts like a set
  773. nbunch = dict.fromkeys(viewer._graph.nbunch_iter(nbunch))
  774. self._nodes_nbrs = lambda: [(n, adjdict[n]) for n in nbunch]
  775. self._nbunch = nbunch
  776. self._data = data
  777. self._default = default
  778. # Set _report based on data and default
  779. if data is True:
  780. if keys is True:
  781. self._report = lambda n, nbr, k, dd: (n, nbr, k, dd)
  782. else:
  783. self._report = lambda n, nbr, k, dd: (n, nbr, dd)
  784. elif data is False:
  785. if keys is True:
  786. self._report = lambda n, nbr, k, dd: (n, nbr, k)
  787. else:
  788. self._report = lambda n, nbr, k, dd: (n, nbr)
  789. else: # data is attribute name
  790. if keys is True:
  791. self._report = (
  792. lambda n, nbr, k, dd: (n, nbr, k, dd[data])
  793. if data in dd
  794. else (n, nbr, k, default)
  795. )
  796. else:
  797. self._report = (
  798. lambda n, nbr, k, dd: (n, nbr, dd[data])
  799. if data in dd
  800. else (n, nbr, default)
  801. )
  802. def __len__(self):
  803. return sum(1 for e in self)
  804. def __iter__(self):
  805. return (
  806. self._report(n, nbr, k, dd)
  807. for n, nbrs in self._nodes_nbrs()
  808. for nbr, kd in nbrs.items()
  809. for k, dd in kd.items()
  810. )
  811. def __contains__(self, e):
  812. u, v = e[:2]
  813. if self._nbunch is not None and u not in self._nbunch:
  814. return False # this edge doesn't start in nbunch
  815. try:
  816. kdict = self._adjdict[u][v]
  817. except KeyError:
  818. return False
  819. if self.keys is True:
  820. k = e[2]
  821. try:
  822. dd = kdict[k]
  823. except KeyError:
  824. return False
  825. return e == self._report(u, v, k, dd)
  826. return any(e == self._report(u, v, k, dd) for k, dd in kdict.items())
  827. class MultiEdgeDataView(OutMultiEdgeDataView):
  828. """An EdgeDataView class for edges of MultiGraph; See EdgeDataView"""
  829. __slots__ = ()
  830. def __iter__(self):
  831. seen = {}
  832. for n, nbrs in self._nodes_nbrs():
  833. for nbr, kd in nbrs.items():
  834. if nbr not in seen:
  835. for k, dd in kd.items():
  836. yield self._report(n, nbr, k, dd)
  837. seen[n] = 1
  838. del seen
  839. def __contains__(self, e):
  840. u, v = e[:2]
  841. if self._nbunch is not None and u not in self._nbunch and v not in self._nbunch:
  842. return False # this edge doesn't start and doesn't end in nbunch
  843. try:
  844. kdict = self._adjdict[u][v]
  845. except KeyError:
  846. try:
  847. kdict = self._adjdict[v][u]
  848. except KeyError:
  849. return False
  850. if self.keys is True:
  851. k = e[2]
  852. try:
  853. dd = kdict[k]
  854. except KeyError:
  855. return False
  856. return e == self._report(u, v, k, dd)
  857. return any(e == self._report(u, v, k, dd) for k, dd in kdict.items())
  858. class InMultiEdgeDataView(OutMultiEdgeDataView):
  859. """An EdgeDataView for inward edges of MultiDiGraph; See EdgeDataView"""
  860. __slots__ = ()
  861. def __iter__(self):
  862. return (
  863. self._report(nbr, n, k, dd)
  864. for n, nbrs in self._nodes_nbrs()
  865. for nbr, kd in nbrs.items()
  866. for k, dd in kd.items()
  867. )
  868. def __contains__(self, e):
  869. u, v = e[:2]
  870. if self._nbunch is not None and v not in self._nbunch:
  871. return False # this edge doesn't end in nbunch
  872. try:
  873. kdict = self._adjdict[v][u]
  874. except KeyError:
  875. return False
  876. if self.keys is True:
  877. k = e[2]
  878. dd = kdict[k]
  879. return e == self._report(u, v, k, dd)
  880. return any(e == self._report(u, v, k, dd) for k, dd in kdict.items())
  881. # EdgeViews have set operations and no data reported
  882. class OutEdgeView(Set, Mapping, EdgeViewABC):
  883. """A EdgeView class for outward edges of a DiGraph"""
  884. __slots__ = ("_adjdict", "_graph", "_nodes_nbrs")
  885. def __getstate__(self):
  886. return {"_graph": self._graph, "_adjdict": self._adjdict}
  887. def __setstate__(self, state):
  888. self._graph = state["_graph"]
  889. self._adjdict = state["_adjdict"]
  890. self._nodes_nbrs = self._adjdict.items
  891. @classmethod
  892. def _from_iterable(cls, it):
  893. return set(it)
  894. dataview = OutEdgeDataView
  895. def __init__(self, G):
  896. self._graph = G
  897. self._adjdict = G._succ if hasattr(G, "succ") else G._adj
  898. self._nodes_nbrs = self._adjdict.items
  899. # Set methods
  900. def __len__(self):
  901. return sum(len(nbrs) for n, nbrs in self._nodes_nbrs())
  902. def __iter__(self):
  903. for n, nbrs in self._nodes_nbrs():
  904. for nbr in nbrs:
  905. yield (n, nbr)
  906. def __contains__(self, e):
  907. try:
  908. u, v = e
  909. return v in self._adjdict[u]
  910. except KeyError:
  911. return False
  912. # Mapping Methods
  913. def __getitem__(self, e):
  914. if isinstance(e, slice):
  915. raise nx.NetworkXError(
  916. f"{type(self).__name__} does not support slicing, "
  917. f"try list(G.edges)[{e.start}:{e.stop}:{e.step}]"
  918. )
  919. u, v = e
  920. try:
  921. return self._adjdict[u][v]
  922. except KeyError as ex: # Customize msg to indicate exception origin
  923. raise KeyError(f"The edge {e} is not in the graph.")
  924. # EdgeDataView methods
  925. def __call__(self, nbunch=None, data=False, *, default=None):
  926. if nbunch is None and data is False:
  927. return self
  928. return self.dataview(self, nbunch, data, default=default)
  929. def data(self, data=True, default=None, nbunch=None):
  930. """
  931. Return a read-only view of edge data.
  932. Parameters
  933. ----------
  934. data : bool or edge attribute key
  935. If ``data=True``, then the data view maps each edge to a dictionary
  936. containing all of its attributes. If `data` is a key in the edge
  937. dictionary, then the data view maps each edge to its value for
  938. the keyed attribute. In this case, if the edge doesn't have the
  939. attribute, the `default` value is returned.
  940. default : object, default=None
  941. The value used when an edge does not have a specific attribute
  942. nbunch : container of nodes, optional (default=None)
  943. Allows restriction to edges only involving certain nodes. All edges
  944. are considered by default.
  945. Returns
  946. -------
  947. dataview
  948. Returns an `EdgeDataView` for undirected Graphs, `OutEdgeDataView`
  949. for DiGraphs, `MultiEdgeDataView` for MultiGraphs and
  950. `OutMultiEdgeDataView` for MultiDiGraphs.
  951. Notes
  952. -----
  953. If ``data=False``, returns an `EdgeView` without any edge data.
  954. See Also
  955. --------
  956. EdgeDataView
  957. OutEdgeDataView
  958. MultiEdgeDataView
  959. OutMultiEdgeDataView
  960. Examples
  961. --------
  962. >>> G = nx.Graph()
  963. >>> G.add_edges_from(
  964. ... [
  965. ... (0, 1, {"dist": 3, "capacity": 20}),
  966. ... (1, 2, {"dist": 4}),
  967. ... (2, 0, {"dist": 5}),
  968. ... ]
  969. ... )
  970. Accessing edge data with ``data=True`` (the default) returns an
  971. edge data view object listing each edge with all of its attributes:
  972. >>> G.edges.data()
  973. EdgeDataView([(0, 1, {'dist': 3, 'capacity': 20}), (0, 2, {'dist': 5}), (1, 2, {'dist': 4})])
  974. If `data` represents a key in the edge attribute dict, a dataview listing
  975. each edge with its value for that specific key is returned:
  976. >>> G.edges.data("dist")
  977. EdgeDataView([(0, 1, 3), (0, 2, 5), (1, 2, 4)])
  978. `nbunch` can be used to limit the edges:
  979. >>> G.edges.data("dist", nbunch=[0])
  980. EdgeDataView([(0, 1, 3), (0, 2, 5)])
  981. If a specific key is not found in an edge attribute dict, the value
  982. specified by `default` is used:
  983. >>> G.edges.data("capacity")
  984. EdgeDataView([(0, 1, 20), (0, 2, None), (1, 2, None)])
  985. Note that there is no check that the `data` key is present in any of
  986. the edge attribute dictionaries:
  987. >>> G.edges.data("speed")
  988. EdgeDataView([(0, 1, None), (0, 2, None), (1, 2, None)])
  989. """
  990. if nbunch is None and data is False:
  991. return self
  992. return self.dataview(self, nbunch, data, default=default)
  993. # String Methods
  994. def __str__(self):
  995. return str(list(self))
  996. def __repr__(self):
  997. return f"{self.__class__.__name__}({list(self)})"
  998. class EdgeView(OutEdgeView):
  999. """A EdgeView class for edges of a Graph
  1000. This densely packed View allows iteration over edges, data lookup
  1001. like a dict and set operations on edges represented by node-tuples.
  1002. In addition, edge data can be controlled by calling this object
  1003. possibly creating an EdgeDataView. Typically edges are iterated over
  1004. and reported as `(u, v)` node tuples or `(u, v, key)` node/key tuples
  1005. for multigraphs. Those edge representations can also be using to
  1006. lookup the data dict for any edge. Set operations also are available
  1007. where those tuples are the elements of the set.
  1008. Calling this object with optional arguments `data`, `default` and `keys`
  1009. controls the form of the tuple (see EdgeDataView). Optional argument
  1010. `nbunch` allows restriction to edges only involving certain nodes.
  1011. If `data is False` (the default) then iterate over 2-tuples `(u, v)`.
  1012. If `data is True` iterate over 3-tuples `(u, v, datadict)`.
  1013. Otherwise iterate over `(u, v, datadict.get(data, default))`.
  1014. For Multigraphs, if `keys is True`, replace `u, v` with `u, v, key` above.
  1015. Parameters
  1016. ==========
  1017. graph : NetworkX graph-like class
  1018. nbunch : (default= all nodes in graph) only report edges with these nodes
  1019. keys : (only for MultiGraph. default=False) report edge key in tuple
  1020. data : bool or string (default=False) see above
  1021. default : object (default=None)
  1022. Examples
  1023. ========
  1024. >>> G = nx.path_graph(4)
  1025. >>> EV = G.edges()
  1026. >>> (2, 3) in EV
  1027. True
  1028. >>> for u, v in EV:
  1029. ... print((u, v))
  1030. (0, 1)
  1031. (1, 2)
  1032. (2, 3)
  1033. >>> assert EV & {(1, 2), (3, 4)} == {(1, 2)}
  1034. >>> EVdata = G.edges(data="color", default="aqua")
  1035. >>> G.add_edge(2, 3, color="blue")
  1036. >>> assert (2, 3, "blue") in EVdata
  1037. >>> for u, v, c in EVdata:
  1038. ... print(f"({u}, {v}) has color: {c}")
  1039. (0, 1) has color: aqua
  1040. (1, 2) has color: aqua
  1041. (2, 3) has color: blue
  1042. >>> EVnbunch = G.edges(nbunch=2)
  1043. >>> assert (2, 3) in EVnbunch
  1044. >>> assert (0, 1) not in EVnbunch
  1045. >>> for u, v in EVnbunch:
  1046. ... assert u == 2 or v == 2
  1047. >>> MG = nx.path_graph(4, create_using=nx.MultiGraph)
  1048. >>> EVmulti = MG.edges(keys=True)
  1049. >>> (2, 3, 0) in EVmulti
  1050. True
  1051. >>> (2, 3) in EVmulti # 2-tuples work even when keys is True
  1052. True
  1053. >>> key = MG.add_edge(2, 3)
  1054. >>> for u, v, k in EVmulti:
  1055. ... print((u, v, k))
  1056. (0, 1, 0)
  1057. (1, 2, 0)
  1058. (2, 3, 0)
  1059. (2, 3, 1)
  1060. """
  1061. __slots__ = ()
  1062. dataview = EdgeDataView
  1063. def __len__(self):
  1064. num_nbrs = (len(nbrs) + (n in nbrs) for n, nbrs in self._nodes_nbrs())
  1065. return sum(num_nbrs) // 2
  1066. def __iter__(self):
  1067. seen = {}
  1068. for n, nbrs in self._nodes_nbrs():
  1069. for nbr in list(nbrs):
  1070. if nbr not in seen:
  1071. yield (n, nbr)
  1072. seen[n] = 1
  1073. del seen
  1074. def __contains__(self, e):
  1075. try:
  1076. u, v = e[:2]
  1077. return v in self._adjdict[u] or u in self._adjdict[v]
  1078. except (KeyError, ValueError):
  1079. return False
  1080. class InEdgeView(OutEdgeView):
  1081. """A EdgeView class for inward edges of a DiGraph"""
  1082. __slots__ = ()
  1083. def __setstate__(self, state):
  1084. self._graph = state["_graph"]
  1085. self._adjdict = state["_adjdict"]
  1086. self._nodes_nbrs = self._adjdict.items
  1087. dataview = InEdgeDataView
  1088. def __init__(self, G):
  1089. self._graph = G
  1090. self._adjdict = G._pred if hasattr(G, "pred") else G._adj
  1091. self._nodes_nbrs = self._adjdict.items
  1092. def __iter__(self):
  1093. for n, nbrs in self._nodes_nbrs():
  1094. for nbr in nbrs:
  1095. yield (nbr, n)
  1096. def __contains__(self, e):
  1097. try:
  1098. u, v = e
  1099. return u in self._adjdict[v]
  1100. except KeyError:
  1101. return False
  1102. def __getitem__(self, e):
  1103. if isinstance(e, slice):
  1104. raise nx.NetworkXError(
  1105. f"{type(self).__name__} does not support slicing, "
  1106. f"try list(G.in_edges)[{e.start}:{e.stop}:{e.step}]"
  1107. )
  1108. u, v = e
  1109. return self._adjdict[v][u]
  1110. class OutMultiEdgeView(OutEdgeView):
  1111. """A EdgeView class for outward edges of a MultiDiGraph"""
  1112. __slots__ = ()
  1113. dataview = OutMultiEdgeDataView
  1114. def __len__(self):
  1115. return sum(
  1116. len(kdict) for n, nbrs in self._nodes_nbrs() for nbr, kdict in nbrs.items()
  1117. )
  1118. def __iter__(self):
  1119. for n, nbrs in self._nodes_nbrs():
  1120. for nbr, kdict in nbrs.items():
  1121. for key in kdict:
  1122. yield (n, nbr, key)
  1123. def __contains__(self, e):
  1124. N = len(e)
  1125. if N == 3:
  1126. u, v, k = e
  1127. elif N == 2:
  1128. u, v = e
  1129. k = 0
  1130. else:
  1131. raise ValueError("MultiEdge must have length 2 or 3")
  1132. try:
  1133. return k in self._adjdict[u][v]
  1134. except KeyError:
  1135. return False
  1136. def __getitem__(self, e):
  1137. if isinstance(e, slice):
  1138. raise nx.NetworkXError(
  1139. f"{type(self).__name__} does not support slicing, "
  1140. f"try list(G.edges)[{e.start}:{e.stop}:{e.step}]"
  1141. )
  1142. u, v, k = e
  1143. return self._adjdict[u][v][k]
  1144. def __call__(self, nbunch=None, data=False, *, default=None, keys=False):
  1145. if nbunch is None and data is False and keys is True:
  1146. return self
  1147. return self.dataview(self, nbunch, data, default=default, keys=keys)
  1148. def data(self, data=True, default=None, nbunch=None, keys=False):
  1149. if nbunch is None and data is False and keys is True:
  1150. return self
  1151. return self.dataview(self, nbunch, data, default=default, keys=keys)
  1152. class MultiEdgeView(OutMultiEdgeView):
  1153. """A EdgeView class for edges of a MultiGraph"""
  1154. __slots__ = ()
  1155. dataview = MultiEdgeDataView
  1156. def __len__(self):
  1157. return sum(1 for e in self)
  1158. def __iter__(self):
  1159. seen = {}
  1160. for n, nbrs in self._nodes_nbrs():
  1161. for nbr, kd in nbrs.items():
  1162. if nbr not in seen:
  1163. for k, dd in kd.items():
  1164. yield (n, nbr, k)
  1165. seen[n] = 1
  1166. del seen
  1167. class InMultiEdgeView(OutMultiEdgeView):
  1168. """A EdgeView class for inward edges of a MultiDiGraph"""
  1169. __slots__ = ()
  1170. def __setstate__(self, state):
  1171. self._graph = state["_graph"]
  1172. self._adjdict = state["_adjdict"]
  1173. self._nodes_nbrs = self._adjdict.items
  1174. dataview = InMultiEdgeDataView
  1175. def __init__(self, G):
  1176. self._graph = G
  1177. self._adjdict = G._pred if hasattr(G, "pred") else G._adj
  1178. self._nodes_nbrs = self._adjdict.items
  1179. def __iter__(self):
  1180. for n, nbrs in self._nodes_nbrs():
  1181. for nbr, kdict in nbrs.items():
  1182. for key in kdict:
  1183. yield (nbr, n, key)
  1184. def __contains__(self, e):
  1185. N = len(e)
  1186. if N == 3:
  1187. u, v, k = e
  1188. elif N == 2:
  1189. u, v = e
  1190. k = 0
  1191. else:
  1192. raise ValueError("MultiEdge must have length 2 or 3")
  1193. try:
  1194. return k in self._adjdict[v][u]
  1195. except KeyError:
  1196. return False
  1197. def __getitem__(self, e):
  1198. if isinstance(e, slice):
  1199. raise nx.NetworkXError(
  1200. f"{type(self).__name__} does not support slicing, "
  1201. f"try list(G.in_edges)[{e.start}:{e.stop}:{e.step}]"
  1202. )
  1203. u, v, k = e
  1204. return self._adjdict[v][u][k]