digraph.py 46 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323
  1. """Base class for directed graphs."""
  2. from copy import deepcopy
  3. from functools import cached_property
  4. import networkx as nx
  5. from networkx import convert
  6. from networkx.classes.coreviews import AdjacencyView
  7. from networkx.classes.graph import Graph
  8. from networkx.classes.reportviews import (
  9. DiDegreeView,
  10. InDegreeView,
  11. InEdgeView,
  12. OutDegreeView,
  13. OutEdgeView,
  14. )
  15. from networkx.exception import NetworkXError
  16. __all__ = ["DiGraph"]
  17. class _CachedPropertyResetterAdjAndSucc:
  18. """Data Descriptor class that syncs and resets cached properties adj and succ
  19. The cached properties `adj` and `succ` are reset whenever `_adj` or `_succ`
  20. are set to new objects. In addition, the attributes `_succ` and `_adj`
  21. are synced so these two names point to the same object.
  22. This object sits on a class and ensures that any instance of that
  23. class clears its cached properties "succ" and "adj" whenever the
  24. underlying instance attributes "_succ" or "_adj" are set to a new object.
  25. It only affects the set process of the obj._adj and obj._succ attribute.
  26. All get/del operations act as they normally would.
  27. For info on Data Descriptors see: https://docs.python.org/3/howto/descriptor.html
  28. """
  29. def __set__(self, obj, value):
  30. od = obj.__dict__
  31. od["_adj"] = value
  32. od["_succ"] = value
  33. # reset cached properties
  34. if "adj" in od:
  35. del od["adj"]
  36. if "succ" in od:
  37. del od["succ"]
  38. class _CachedPropertyResetterPred:
  39. """Data Descriptor class for _pred that resets ``pred`` cached_property when needed
  40. This assumes that the ``cached_property`` ``G.pred`` should be reset whenever
  41. ``G._pred`` is set to a new value.
  42. This object sits on a class and ensures that any instance of that
  43. class clears its cached property "pred" whenever the underlying
  44. instance attribute "_pred" is set to a new object. It only affects
  45. the set process of the obj._pred attribute. All get/del operations
  46. act as they normally would.
  47. For info on Data Descriptors see: https://docs.python.org/3/howto/descriptor.html
  48. """
  49. def __set__(self, obj, value):
  50. od = obj.__dict__
  51. od["_pred"] = value
  52. if "pred" in od:
  53. del od["pred"]
  54. class DiGraph(Graph):
  55. """
  56. Base class for directed graphs.
  57. A DiGraph stores nodes and edges with optional data, or attributes.
  58. DiGraphs hold directed edges. Self loops are allowed but multiple
  59. (parallel) edges are not.
  60. Nodes can be arbitrary (hashable) Python objects with optional
  61. key/value attributes. By convention `None` is not used as a node.
  62. Edges are represented as links between nodes with optional
  63. key/value attributes.
  64. Parameters
  65. ----------
  66. incoming_graph_data : input graph (optional, default: None)
  67. Data to initialize graph. If None (default) an empty
  68. graph is created. The data can be any format that is supported
  69. by the to_networkx_graph() function, currently including edge list,
  70. dict of dicts, dict of lists, NetworkX graph, 2D NumPy array, SciPy
  71. sparse matrix, or PyGraphviz graph.
  72. attr : keyword arguments, optional (default= no attributes)
  73. Attributes to add to graph as key=value pairs.
  74. See Also
  75. --------
  76. Graph
  77. MultiGraph
  78. MultiDiGraph
  79. Examples
  80. --------
  81. Create an empty graph structure (a "null graph") with no nodes and
  82. no edges.
  83. >>> G = nx.DiGraph()
  84. G can be grown in several ways.
  85. **Nodes:**
  86. Add one node at a time:
  87. >>> G.add_node(1)
  88. Add the nodes from any container (a list, dict, set or
  89. even the lines from a file or the nodes from another graph).
  90. >>> G.add_nodes_from([2, 3])
  91. >>> G.add_nodes_from(range(100, 110))
  92. >>> H = nx.path_graph(10)
  93. >>> G.add_nodes_from(H)
  94. In addition to strings and integers any hashable Python object
  95. (except None) can represent a node, e.g. a customized node object,
  96. or even another Graph.
  97. >>> G.add_node(H)
  98. **Edges:**
  99. G can also be grown by adding edges.
  100. Add one edge,
  101. >>> G.add_edge(1, 2)
  102. a list of edges,
  103. >>> G.add_edges_from([(1, 2), (1, 3)])
  104. or a collection of edges,
  105. >>> G.add_edges_from(H.edges)
  106. If some edges connect nodes not yet in the graph, the nodes
  107. are added automatically. There are no errors when adding
  108. nodes or edges that already exist.
  109. **Attributes:**
  110. Each graph, node, and edge can hold key/value attribute pairs
  111. in an associated attribute dictionary (the keys must be hashable).
  112. By default these are empty, but can be added or changed using
  113. add_edge, add_node or direct manipulation of the attribute
  114. dictionaries named graph, node and edge respectively.
  115. >>> G = nx.DiGraph(day="Friday")
  116. >>> G.graph
  117. {'day': 'Friday'}
  118. Add node attributes using add_node(), add_nodes_from() or G.nodes
  119. >>> G.add_node(1, time="5pm")
  120. >>> G.add_nodes_from([3], time="2pm")
  121. >>> G.nodes[1]
  122. {'time': '5pm'}
  123. >>> G.nodes[1]["room"] = 714
  124. >>> del G.nodes[1]["room"] # remove attribute
  125. >>> list(G.nodes(data=True))
  126. [(1, {'time': '5pm'}), (3, {'time': '2pm'})]
  127. Add edge attributes using add_edge(), add_edges_from(), subscript
  128. notation, or G.edges.
  129. >>> G.add_edge(1, 2, weight=4.7)
  130. >>> G.add_edges_from([(3, 4), (4, 5)], color="red")
  131. >>> G.add_edges_from([(1, 2, {"color": "blue"}), (2, 3, {"weight": 8})])
  132. >>> G[1][2]["weight"] = 4.7
  133. >>> G.edges[1, 2]["weight"] = 4
  134. Warning: we protect the graph data structure by making `G.edges[1, 2]` a
  135. read-only dict-like structure. However, you can assign to attributes
  136. in e.g. `G.edges[1, 2]`. Thus, use 2 sets of brackets to add/change
  137. data attributes: `G.edges[1, 2]['weight'] = 4`
  138. (For multigraphs: `MG.edges[u, v, key][name] = value`).
  139. **Shortcuts:**
  140. Many common graph features allow python syntax to speed reporting.
  141. >>> 1 in G # check if node in graph
  142. True
  143. >>> [n for n in G if n < 3] # iterate through nodes
  144. [1, 2]
  145. >>> len(G) # number of nodes in graph
  146. 5
  147. Often the best way to traverse all edges of a graph is via the neighbors.
  148. The neighbors are reported as an adjacency-dict `G.adj` or `G.adjacency()`
  149. >>> for n, nbrsdict in G.adjacency():
  150. ... for nbr, eattr in nbrsdict.items():
  151. ... if "weight" in eattr:
  152. ... # Do something useful with the edges
  153. ... pass
  154. But the edges reporting object is often more convenient:
  155. >>> for u, v, weight in G.edges(data="weight"):
  156. ... if weight is not None:
  157. ... # Do something useful with the edges
  158. ... pass
  159. **Reporting:**
  160. Simple graph information is obtained using object-attributes and methods.
  161. Reporting usually provides views instead of containers to reduce memory
  162. usage. The views update as the graph is updated similarly to dict-views.
  163. The objects `nodes`, `edges` and `adj` provide access to data attributes
  164. via lookup (e.g. `nodes[n]`, `edges[u, v]`, `adj[u][v]`) and iteration
  165. (e.g. `nodes.items()`, `nodes.data('color')`,
  166. `nodes.data('color', default='blue')` and similarly for `edges`)
  167. Views exist for `nodes`, `edges`, `neighbors()`/`adj` and `degree`.
  168. For details on these and other miscellaneous methods, see below.
  169. **Subclasses (Advanced):**
  170. The Graph class uses a dict-of-dict-of-dict data structure.
  171. The outer dict (node_dict) holds adjacency information keyed by node.
  172. The next dict (adjlist_dict) represents the adjacency information and holds
  173. edge data keyed by neighbor. The inner dict (edge_attr_dict) represents
  174. the edge data and holds edge attribute values keyed by attribute names.
  175. Each of these three dicts can be replaced in a subclass by a user defined
  176. dict-like object. In general, the dict-like features should be
  177. maintained but extra features can be added. To replace one of the
  178. dicts create a new graph class by changing the class(!) variable
  179. holding the factory for that dict-like structure. The variable names are
  180. node_dict_factory, node_attr_dict_factory, adjlist_inner_dict_factory,
  181. adjlist_outer_dict_factory, edge_attr_dict_factory and graph_attr_dict_factory.
  182. node_dict_factory : function, (default: dict)
  183. Factory function to be used to create the dict containing node
  184. attributes, keyed by node id.
  185. It should require no arguments and return a dict-like object
  186. node_attr_dict_factory: function, (default: dict)
  187. Factory function to be used to create the node attribute
  188. dict which holds attribute values keyed by attribute name.
  189. It should require no arguments and return a dict-like object
  190. adjlist_outer_dict_factory : function, (default: dict)
  191. Factory function to be used to create the outer-most dict
  192. in the data structure that holds adjacency info keyed by node.
  193. It should require no arguments and return a dict-like object.
  194. adjlist_inner_dict_factory : function, optional (default: dict)
  195. Factory function to be used to create the adjacency list
  196. dict which holds edge data keyed by neighbor.
  197. It should require no arguments and return a dict-like object
  198. edge_attr_dict_factory : function, optional (default: dict)
  199. Factory function to be used to create the edge attribute
  200. dict which holds attribute values keyed by attribute name.
  201. It should require no arguments and return a dict-like object.
  202. graph_attr_dict_factory : function, (default: dict)
  203. Factory function to be used to create the graph attribute
  204. dict which holds attribute values keyed by attribute name.
  205. It should require no arguments and return a dict-like object.
  206. Typically, if your extension doesn't impact the data structure all
  207. methods will inherited without issue except: `to_directed/to_undirected`.
  208. By default these methods create a DiGraph/Graph class and you probably
  209. want them to create your extension of a DiGraph/Graph. To facilitate
  210. this we define two class variables that you can set in your subclass.
  211. to_directed_class : callable, (default: DiGraph or MultiDiGraph)
  212. Class to create a new graph structure in the `to_directed` method.
  213. If `None`, a NetworkX class (DiGraph or MultiDiGraph) is used.
  214. to_undirected_class : callable, (default: Graph or MultiGraph)
  215. Class to create a new graph structure in the `to_undirected` method.
  216. If `None`, a NetworkX class (Graph or MultiGraph) is used.
  217. **Subclassing Example**
  218. Create a low memory graph class that effectively disallows edge
  219. attributes by using a single attribute dict for all edges.
  220. This reduces the memory used, but you lose edge attributes.
  221. >>> class ThinGraph(nx.Graph):
  222. ... all_edge_dict = {"weight": 1}
  223. ...
  224. ... def single_edge_dict(self):
  225. ... return self.all_edge_dict
  226. ...
  227. ... edge_attr_dict_factory = single_edge_dict
  228. >>> G = ThinGraph()
  229. >>> G.add_edge(2, 1)
  230. >>> G[2][1]
  231. {'weight': 1}
  232. >>> G.add_edge(2, 2)
  233. >>> G[2][1] is G[2][2]
  234. True
  235. """
  236. _adj = _CachedPropertyResetterAdjAndSucc() # type: ignore[assignment]
  237. _succ = _adj # type: ignore[has-type]
  238. _pred = _CachedPropertyResetterPred()
  239. def __init__(self, incoming_graph_data=None, **attr):
  240. """Initialize a graph with edges, name, or graph attributes.
  241. Parameters
  242. ----------
  243. incoming_graph_data : input graph (optional, default: None)
  244. Data to initialize graph. If None (default) an empty
  245. graph is created. The data can be an edge list, or any
  246. NetworkX graph object. If the corresponding optional Python
  247. packages are installed the data can also be a 2D NumPy array, a
  248. SciPy sparse array, or a PyGraphviz graph.
  249. attr : keyword arguments, optional (default= no attributes)
  250. Attributes to add to graph as key=value pairs.
  251. See Also
  252. --------
  253. convert
  254. Examples
  255. --------
  256. >>> G = nx.Graph() # or DiGraph, MultiGraph, MultiDiGraph, etc
  257. >>> G = nx.Graph(name="my graph")
  258. >>> e = [(1, 2), (2, 3), (3, 4)] # list of edges
  259. >>> G = nx.Graph(e)
  260. Arbitrary graph attribute pairs (key=value) may be assigned
  261. >>> G = nx.Graph(e, day="Friday")
  262. >>> G.graph
  263. {'day': 'Friday'}
  264. """
  265. self.graph = self.graph_attr_dict_factory() # dictionary for graph attributes
  266. self._node = self.node_dict_factory() # dictionary for node attr
  267. # We store two adjacency lists:
  268. # the predecessors of node n are stored in the dict self._pred
  269. # the successors of node n are stored in the dict self._succ=self._adj
  270. self._adj = self.adjlist_outer_dict_factory() # empty adjacency dict successor
  271. self._pred = self.adjlist_outer_dict_factory() # predecessor
  272. # Note: self._succ = self._adj # successor
  273. # attempt to load graph with data
  274. if incoming_graph_data is not None:
  275. convert.to_networkx_graph(incoming_graph_data, create_using=self)
  276. # load graph attributes (must be after convert)
  277. self.graph.update(attr)
  278. @cached_property
  279. def adj(self):
  280. """Graph adjacency object holding the neighbors of each node.
  281. This object is a read-only dict-like structure with node keys
  282. and neighbor-dict values. The neighbor-dict is keyed by neighbor
  283. to the edge-data-dict. So `G.adj[3][2]['color'] = 'blue'` sets
  284. the color of the edge `(3, 2)` to `"blue"`.
  285. Iterating over G.adj behaves like a dict. Useful idioms include
  286. `for nbr, datadict in G.adj[n].items():`.
  287. The neighbor information is also provided by subscripting the graph.
  288. So `for nbr, foovalue in G[node].data('foo', default=1):` works.
  289. For directed graphs, `G.adj` holds outgoing (successor) info.
  290. """
  291. return AdjacencyView(self._succ)
  292. @cached_property
  293. def succ(self):
  294. """Graph adjacency object holding the successors of each node.
  295. This object is a read-only dict-like structure with node keys
  296. and neighbor-dict values. The neighbor-dict is keyed by neighbor
  297. to the edge-data-dict. So `G.succ[3][2]['color'] = 'blue'` sets
  298. the color of the edge `(3, 2)` to `"blue"`.
  299. Iterating over G.succ behaves like a dict. Useful idioms include
  300. `for nbr, datadict in G.succ[n].items():`. A data-view not provided
  301. by dicts also exists: `for nbr, foovalue in G.succ[node].data('foo'):`
  302. and a default can be set via a `default` argument to the `data` method.
  303. The neighbor information is also provided by subscripting the graph.
  304. So `for nbr, foovalue in G[node].data('foo', default=1):` works.
  305. For directed graphs, `G.adj` is identical to `G.succ`.
  306. """
  307. return AdjacencyView(self._succ)
  308. @cached_property
  309. def pred(self):
  310. """Graph adjacency object holding the predecessors of each node.
  311. This object is a read-only dict-like structure with node keys
  312. and neighbor-dict values. The neighbor-dict is keyed by neighbor
  313. to the edge-data-dict. So `G.pred[2][3]['color'] = 'blue'` sets
  314. the color of the edge `(3, 2)` to `"blue"`.
  315. Iterating over G.pred behaves like a dict. Useful idioms include
  316. `for nbr, datadict in G.pred[n].items():`. A data-view not provided
  317. by dicts also exists: `for nbr, foovalue in G.pred[node].data('foo'):`
  318. A default can be set via a `default` argument to the `data` method.
  319. """
  320. return AdjacencyView(self._pred)
  321. def add_node(self, node_for_adding, **attr):
  322. """Add a single node `node_for_adding` and update node attributes.
  323. Parameters
  324. ----------
  325. node_for_adding : node
  326. A node can be any hashable Python object except None.
  327. attr : keyword arguments, optional
  328. Set or change node attributes using key=value.
  329. See Also
  330. --------
  331. add_nodes_from
  332. Examples
  333. --------
  334. >>> G = nx.Graph() # or DiGraph, MultiGraph, MultiDiGraph, etc
  335. >>> G.add_node(1)
  336. >>> G.add_node("Hello")
  337. >>> K3 = nx.Graph([(0, 1), (1, 2), (2, 0)])
  338. >>> G.add_node(K3)
  339. >>> G.number_of_nodes()
  340. 3
  341. Use keywords set/change node attributes:
  342. >>> G.add_node(1, size=10)
  343. >>> G.add_node(3, weight=0.4, UTM=("13S", 382871, 3972649))
  344. Notes
  345. -----
  346. A hashable object is one that can be used as a key in a Python
  347. dictionary. This includes strings, numbers, tuples of strings
  348. and numbers, etc.
  349. On many platforms hashable items also include mutables such as
  350. NetworkX Graphs, though one should be careful that the hash
  351. doesn't change on mutables.
  352. """
  353. if node_for_adding not in self._succ:
  354. if node_for_adding is None:
  355. raise ValueError("None cannot be a node")
  356. self._succ[node_for_adding] = self.adjlist_inner_dict_factory()
  357. self._pred[node_for_adding] = self.adjlist_inner_dict_factory()
  358. attr_dict = self._node[node_for_adding] = self.node_attr_dict_factory()
  359. attr_dict.update(attr)
  360. else: # update attr even if node already exists
  361. self._node[node_for_adding].update(attr)
  362. def add_nodes_from(self, nodes_for_adding, **attr):
  363. """Add multiple nodes.
  364. Parameters
  365. ----------
  366. nodes_for_adding : iterable container
  367. A container of nodes (list, dict, set, etc.).
  368. OR
  369. A container of (node, attribute dict) tuples.
  370. Node attributes are updated using the attribute dict.
  371. attr : keyword arguments, optional (default= no attributes)
  372. Update attributes for all nodes in nodes.
  373. Node attributes specified in nodes as a tuple take
  374. precedence over attributes specified via keyword arguments.
  375. See Also
  376. --------
  377. add_node
  378. Notes
  379. -----
  380. When adding nodes from an iterator over the graph you are changing,
  381. a `RuntimeError` can be raised with message:
  382. `RuntimeError: dictionary changed size during iteration`. This
  383. happens when the graph's underlying dictionary is modified during
  384. iteration. To avoid this error, evaluate the iterator into a separate
  385. object, e.g. by using `list(iterator_of_nodes)`, and pass this
  386. object to `G.add_nodes_from`.
  387. Examples
  388. --------
  389. >>> G = nx.Graph() # or DiGraph, MultiGraph, MultiDiGraph, etc
  390. >>> G.add_nodes_from("Hello")
  391. >>> K3 = nx.Graph([(0, 1), (1, 2), (2, 0)])
  392. >>> G.add_nodes_from(K3)
  393. >>> sorted(G.nodes(), key=str)
  394. [0, 1, 2, 'H', 'e', 'l', 'o']
  395. Use keywords to update specific node attributes for every node.
  396. >>> G.add_nodes_from([1, 2], size=10)
  397. >>> G.add_nodes_from([3, 4], weight=0.4)
  398. Use (node, attrdict) tuples to update attributes for specific nodes.
  399. >>> G.add_nodes_from([(1, dict(size=11)), (2, {"color": "blue"})])
  400. >>> G.nodes[1]["size"]
  401. 11
  402. >>> H = nx.Graph()
  403. >>> H.add_nodes_from(G.nodes(data=True))
  404. >>> H.nodes[1]["size"]
  405. 11
  406. Evaluate an iterator over a graph if using it to modify the same graph
  407. >>> G = nx.DiGraph([(0, 1), (1, 2), (3, 4)])
  408. >>> # wrong way - will raise RuntimeError
  409. >>> # G.add_nodes_from(n + 1 for n in G.nodes)
  410. >>> # correct way
  411. >>> G.add_nodes_from(list(n + 1 for n in G.nodes))
  412. """
  413. for n in nodes_for_adding:
  414. try:
  415. newnode = n not in self._node
  416. newdict = attr
  417. except TypeError:
  418. n, ndict = n
  419. newnode = n not in self._node
  420. newdict = attr.copy()
  421. newdict.update(ndict)
  422. if newnode:
  423. if n is None:
  424. raise ValueError("None cannot be a node")
  425. self._succ[n] = self.adjlist_inner_dict_factory()
  426. self._pred[n] = self.adjlist_inner_dict_factory()
  427. self._node[n] = self.node_attr_dict_factory()
  428. self._node[n].update(newdict)
  429. def remove_node(self, n):
  430. """Remove node n.
  431. Removes the node n and all adjacent edges.
  432. Attempting to remove a non-existent node will raise an exception.
  433. Parameters
  434. ----------
  435. n : node
  436. A node in the graph
  437. Raises
  438. ------
  439. NetworkXError
  440. If n is not in the graph.
  441. See Also
  442. --------
  443. remove_nodes_from
  444. Examples
  445. --------
  446. >>> G = nx.path_graph(3) # or DiGraph, MultiGraph, MultiDiGraph, etc
  447. >>> list(G.edges)
  448. [(0, 1), (1, 2)]
  449. >>> G.remove_node(1)
  450. >>> list(G.edges)
  451. []
  452. """
  453. try:
  454. nbrs = self._succ[n]
  455. del self._node[n]
  456. except KeyError as err: # NetworkXError if n not in self
  457. raise NetworkXError(f"The node {n} is not in the digraph.") from err
  458. for u in nbrs:
  459. del self._pred[u][n] # remove all edges n-u in digraph
  460. del self._succ[n] # remove node from succ
  461. for u in self._pred[n]:
  462. del self._succ[u][n] # remove all edges n-u in digraph
  463. del self._pred[n] # remove node from pred
  464. def remove_nodes_from(self, nodes):
  465. """Remove multiple nodes.
  466. Parameters
  467. ----------
  468. nodes : iterable container
  469. A container of nodes (list, dict, set, etc.). If a node
  470. in the container is not in the graph it is silently ignored.
  471. See Also
  472. --------
  473. remove_node
  474. Notes
  475. -----
  476. When removing nodes from an iterator over the graph you are changing,
  477. a `RuntimeError` will be raised with message:
  478. `RuntimeError: dictionary changed size during iteration`. This
  479. happens when the graph's underlying dictionary is modified during
  480. iteration. To avoid this error, evaluate the iterator into a separate
  481. object, e.g. by using `list(iterator_of_nodes)`, and pass this
  482. object to `G.remove_nodes_from`.
  483. Examples
  484. --------
  485. >>> G = nx.path_graph(3) # or DiGraph, MultiGraph, MultiDiGraph, etc
  486. >>> e = list(G.nodes)
  487. >>> e
  488. [0, 1, 2]
  489. >>> G.remove_nodes_from(e)
  490. >>> list(G.nodes)
  491. []
  492. Evaluate an iterator over a graph if using it to modify the same graph
  493. >>> G = nx.DiGraph([(0, 1), (1, 2), (3, 4)])
  494. >>> # this command will fail, as the graph's dict is modified during iteration
  495. >>> # G.remove_nodes_from(n for n in G.nodes if n < 2)
  496. >>> # this command will work, since the dictionary underlying graph is not modified
  497. >>> G.remove_nodes_from(list(n for n in G.nodes if n < 2))
  498. """
  499. for n in nodes:
  500. try:
  501. succs = self._succ[n]
  502. del self._node[n]
  503. for u in succs:
  504. del self._pred[u][n] # remove all edges n-u in digraph
  505. del self._succ[n] # now remove node
  506. for u in self._pred[n]:
  507. del self._succ[u][n] # remove all edges n-u in digraph
  508. del self._pred[n] # now remove node
  509. except KeyError:
  510. pass # silent failure on remove
  511. def add_edge(self, u_of_edge, v_of_edge, **attr):
  512. """Add an edge between u and v.
  513. The nodes u and v will be automatically added if they are
  514. not already in the graph.
  515. Edge attributes can be specified with keywords or by directly
  516. accessing the edge's attribute dictionary. See examples below.
  517. Parameters
  518. ----------
  519. u_of_edge, v_of_edge : nodes
  520. Nodes can be, for example, strings or numbers.
  521. Nodes must be hashable (and not None) Python objects.
  522. attr : keyword arguments, optional
  523. Edge data (or labels or objects) can be assigned using
  524. keyword arguments.
  525. See Also
  526. --------
  527. add_edges_from : add a collection of edges
  528. Notes
  529. -----
  530. Adding an edge that already exists updates the edge data.
  531. Many NetworkX algorithms designed for weighted graphs use
  532. an edge attribute (by default `weight`) to hold a numerical value.
  533. Examples
  534. --------
  535. The following all add the edge e=(1, 2) to graph G:
  536. >>> G = nx.Graph() # or DiGraph, MultiGraph, MultiDiGraph, etc
  537. >>> e = (1, 2)
  538. >>> G.add_edge(1, 2) # explicit two-node form
  539. >>> G.add_edge(*e) # single edge as tuple of two nodes
  540. >>> G.add_edges_from([(1, 2)]) # add edges from iterable container
  541. Associate data to edges using keywords:
  542. >>> G.add_edge(1, 2, weight=3)
  543. >>> G.add_edge(1, 3, weight=7, capacity=15, length=342.7)
  544. For non-string attribute keys, use subscript notation.
  545. >>> G.add_edge(1, 2)
  546. >>> G[1][2].update({0: 5})
  547. >>> G.edges[1, 2].update({0: 5})
  548. """
  549. u, v = u_of_edge, v_of_edge
  550. # add nodes
  551. if u not in self._succ:
  552. if u is None:
  553. raise ValueError("None cannot be a node")
  554. self._succ[u] = self.adjlist_inner_dict_factory()
  555. self._pred[u] = self.adjlist_inner_dict_factory()
  556. self._node[u] = self.node_attr_dict_factory()
  557. if v not in self._succ:
  558. if v is None:
  559. raise ValueError("None cannot be a node")
  560. self._succ[v] = self.adjlist_inner_dict_factory()
  561. self._pred[v] = self.adjlist_inner_dict_factory()
  562. self._node[v] = self.node_attr_dict_factory()
  563. # add the edge
  564. datadict = self._adj[u].get(v, self.edge_attr_dict_factory())
  565. datadict.update(attr)
  566. self._succ[u][v] = datadict
  567. self._pred[v][u] = datadict
  568. def add_edges_from(self, ebunch_to_add, **attr):
  569. """Add all the edges in ebunch_to_add.
  570. Parameters
  571. ----------
  572. ebunch_to_add : container of edges
  573. Each edge given in the container will be added to the
  574. graph. The edges must be given as 2-tuples (u, v) or
  575. 3-tuples (u, v, d) where d is a dictionary containing edge data.
  576. attr : keyword arguments, optional
  577. Edge data (or labels or objects) can be assigned using
  578. keyword arguments.
  579. See Also
  580. --------
  581. add_edge : add a single edge
  582. add_weighted_edges_from : convenient way to add weighted edges
  583. Notes
  584. -----
  585. Adding the same edge twice has no effect but any edge data
  586. will be updated when each duplicate edge is added.
  587. Edge attributes specified in an ebunch take precedence over
  588. attributes specified via keyword arguments.
  589. When adding edges from an iterator over the graph you are changing,
  590. a `RuntimeError` can be raised with message:
  591. `RuntimeError: dictionary changed size during iteration`. This
  592. happens when the graph's underlying dictionary is modified during
  593. iteration. To avoid this error, evaluate the iterator into a separate
  594. object, e.g. by using `list(iterator_of_edges)`, and pass this
  595. object to `G.add_edges_from`.
  596. Examples
  597. --------
  598. >>> G = nx.Graph() # or DiGraph, MultiGraph, MultiDiGraph, etc
  599. >>> G.add_edges_from([(0, 1), (1, 2)]) # using a list of edge tuples
  600. >>> e = zip(range(0, 3), range(1, 4))
  601. >>> G.add_edges_from(e) # Add the path graph 0-1-2-3
  602. Associate data to edges
  603. >>> G.add_edges_from([(1, 2), (2, 3)], weight=3)
  604. >>> G.add_edges_from([(3, 4), (1, 4)], label="WN2898")
  605. Evaluate an iterator over a graph if using it to modify the same graph
  606. >>> G = nx.DiGraph([(1, 2), (2, 3), (3, 4)])
  607. >>> # Grow graph by one new node, adding edges to all existing nodes.
  608. >>> # wrong way - will raise RuntimeError
  609. >>> # G.add_edges_from(((5, n) for n in G.nodes))
  610. >>> # right way - note that there will be no self-edge for node 5
  611. >>> G.add_edges_from(list((5, n) for n in G.nodes))
  612. """
  613. for e in ebunch_to_add:
  614. ne = len(e)
  615. if ne == 3:
  616. u, v, dd = e
  617. elif ne == 2:
  618. u, v = e
  619. dd = {}
  620. else:
  621. raise NetworkXError(f"Edge tuple {e} must be a 2-tuple or 3-tuple.")
  622. if u not in self._succ:
  623. if u is None:
  624. raise ValueError("None cannot be a node")
  625. self._succ[u] = self.adjlist_inner_dict_factory()
  626. self._pred[u] = self.adjlist_inner_dict_factory()
  627. self._node[u] = self.node_attr_dict_factory()
  628. if v not in self._succ:
  629. if v is None:
  630. raise ValueError("None cannot be a node")
  631. self._succ[v] = self.adjlist_inner_dict_factory()
  632. self._pred[v] = self.adjlist_inner_dict_factory()
  633. self._node[v] = self.node_attr_dict_factory()
  634. datadict = self._adj[u].get(v, self.edge_attr_dict_factory())
  635. datadict.update(attr)
  636. datadict.update(dd)
  637. self._succ[u][v] = datadict
  638. self._pred[v][u] = datadict
  639. def remove_edge(self, u, v):
  640. """Remove the edge between u and v.
  641. Parameters
  642. ----------
  643. u, v : nodes
  644. Remove the edge between nodes u and v.
  645. Raises
  646. ------
  647. NetworkXError
  648. If there is not an edge between u and v.
  649. See Also
  650. --------
  651. remove_edges_from : remove a collection of edges
  652. Examples
  653. --------
  654. >>> G = nx.Graph() # or DiGraph, etc
  655. >>> nx.add_path(G, [0, 1, 2, 3])
  656. >>> G.remove_edge(0, 1)
  657. >>> e = (1, 2)
  658. >>> G.remove_edge(*e) # unpacks e from an edge tuple
  659. >>> e = (2, 3, {"weight": 7}) # an edge with attribute data
  660. >>> G.remove_edge(*e[:2]) # select first part of edge tuple
  661. """
  662. try:
  663. del self._succ[u][v]
  664. del self._pred[v][u]
  665. except KeyError as err:
  666. raise NetworkXError(f"The edge {u}-{v} not in graph.") from err
  667. def remove_edges_from(self, ebunch):
  668. """Remove all edges specified in ebunch.
  669. Parameters
  670. ----------
  671. ebunch: list or container of edge tuples
  672. Each edge given in the list or container will be removed
  673. from the graph. The edges can be:
  674. - 2-tuples (u, v) edge between u and v.
  675. - 3-tuples (u, v, k) where k is ignored.
  676. See Also
  677. --------
  678. remove_edge : remove a single edge
  679. Notes
  680. -----
  681. Will fail silently if an edge in ebunch is not in the graph.
  682. Examples
  683. --------
  684. >>> G = nx.path_graph(4) # or DiGraph, MultiGraph, MultiDiGraph, etc
  685. >>> ebunch = [(1, 2), (2, 3)]
  686. >>> G.remove_edges_from(ebunch)
  687. """
  688. for e in ebunch:
  689. u, v = e[:2] # ignore edge data
  690. if u in self._succ and v in self._succ[u]:
  691. del self._succ[u][v]
  692. del self._pred[v][u]
  693. def has_successor(self, u, v):
  694. """Returns True if node u has successor v.
  695. This is true if graph has the edge u->v.
  696. """
  697. return u in self._succ and v in self._succ[u]
  698. def has_predecessor(self, u, v):
  699. """Returns True if node u has predecessor v.
  700. This is true if graph has the edge u<-v.
  701. """
  702. return u in self._pred and v in self._pred[u]
  703. def successors(self, n):
  704. """Returns an iterator over successor nodes of n.
  705. A successor of n is a node m such that there exists a directed
  706. edge from n to m.
  707. Parameters
  708. ----------
  709. n : node
  710. A node in the graph
  711. Raises
  712. ------
  713. NetworkXError
  714. If n is not in the graph.
  715. See Also
  716. --------
  717. predecessors
  718. Notes
  719. -----
  720. neighbors() and successors() are the same.
  721. """
  722. try:
  723. return iter(self._succ[n])
  724. except KeyError as err:
  725. raise NetworkXError(f"The node {n} is not in the digraph.") from err
  726. # digraph definitions
  727. neighbors = successors
  728. def predecessors(self, n):
  729. """Returns an iterator over predecessor nodes of n.
  730. A predecessor of n is a node m such that there exists a directed
  731. edge from m to n.
  732. Parameters
  733. ----------
  734. n : node
  735. A node in the graph
  736. Raises
  737. ------
  738. NetworkXError
  739. If n is not in the graph.
  740. See Also
  741. --------
  742. successors
  743. """
  744. try:
  745. return iter(self._pred[n])
  746. except KeyError as err:
  747. raise NetworkXError(f"The node {n} is not in the digraph.") from err
  748. @cached_property
  749. def edges(self):
  750. """An OutEdgeView of the DiGraph as G.edges or G.edges().
  751. edges(self, nbunch=None, data=False, default=None)
  752. The OutEdgeView provides set-like operations on the edge-tuples
  753. as well as edge attribute lookup. When called, it also provides
  754. an EdgeDataView object which allows control of access to edge
  755. attributes (but does not provide set-like operations).
  756. Hence, `G.edges[u, v]['color']` provides the value of the color
  757. attribute for edge `(u, v)` while
  758. `for (u, v, c) in G.edges.data('color', default='red'):`
  759. iterates through all the edges yielding the color attribute
  760. with default `'red'` if no color attribute exists.
  761. Parameters
  762. ----------
  763. nbunch : single node, container, or all nodes (default= all nodes)
  764. The view will only report edges from these nodes.
  765. data : string or bool, optional (default=False)
  766. The edge attribute returned in 3-tuple (u, v, ddict[data]).
  767. If True, return edge attribute dict in 3-tuple (u, v, ddict).
  768. If False, return 2-tuple (u, v).
  769. default : value, optional (default=None)
  770. Value used for edges that don't have the requested attribute.
  771. Only relevant if data is not True or False.
  772. Returns
  773. -------
  774. edges : OutEdgeView
  775. A view of edge attributes, usually it iterates over (u, v)
  776. or (u, v, d) tuples of edges, but can also be used for
  777. attribute lookup as `edges[u, v]['foo']`.
  778. See Also
  779. --------
  780. in_edges, out_edges
  781. Notes
  782. -----
  783. Nodes in nbunch that are not in the graph will be (quietly) ignored.
  784. For directed graphs this returns the out-edges.
  785. Examples
  786. --------
  787. >>> G = nx.DiGraph() # or MultiDiGraph, etc
  788. >>> nx.add_path(G, [0, 1, 2])
  789. >>> G.add_edge(2, 3, weight=5)
  790. >>> [e for e in G.edges]
  791. [(0, 1), (1, 2), (2, 3)]
  792. >>> G.edges.data() # default data is {} (empty dict)
  793. OutEdgeDataView([(0, 1, {}), (1, 2, {}), (2, 3, {'weight': 5})])
  794. >>> G.edges.data("weight", default=1)
  795. OutEdgeDataView([(0, 1, 1), (1, 2, 1), (2, 3, 5)])
  796. >>> G.edges([0, 2]) # only edges originating from these nodes
  797. OutEdgeDataView([(0, 1), (2, 3)])
  798. >>> G.edges(0) # only edges from node 0
  799. OutEdgeDataView([(0, 1)])
  800. """
  801. return OutEdgeView(self)
  802. # alias out_edges to edges
  803. @cached_property
  804. def out_edges(self):
  805. return OutEdgeView(self)
  806. out_edges.__doc__ = edges.__doc__
  807. @cached_property
  808. def in_edges(self):
  809. """A view of the in edges of the graph as G.in_edges or G.in_edges().
  810. in_edges(self, nbunch=None, data=False, default=None):
  811. Parameters
  812. ----------
  813. nbunch : single node, container, or all nodes (default= all nodes)
  814. The view will only report edges incident to these nodes.
  815. data : string or bool, optional (default=False)
  816. The edge attribute returned in 3-tuple (u, v, ddict[data]).
  817. If True, return edge attribute dict in 3-tuple (u, v, ddict).
  818. If False, return 2-tuple (u, v).
  819. default : value, optional (default=None)
  820. Value used for edges that don't have the requested attribute.
  821. Only relevant if data is not True or False.
  822. Returns
  823. -------
  824. in_edges : InEdgeView or InEdgeDataView
  825. A view of edge attributes, usually it iterates over (u, v)
  826. or (u, v, d) tuples of edges, but can also be used for
  827. attribute lookup as `edges[u, v]['foo']`.
  828. Examples
  829. --------
  830. >>> G = nx.DiGraph()
  831. >>> G.add_edge(1, 2, color='blue')
  832. >>> G.in_edges()
  833. InEdgeView([(1, 2)])
  834. >>> G.in_edges(nbunch=2)
  835. InEdgeDataView([(1, 2)])
  836. See Also
  837. --------
  838. edges
  839. """
  840. return InEdgeView(self)
  841. @cached_property
  842. def degree(self):
  843. """A DegreeView for the Graph as G.degree or G.degree().
  844. The node degree is the number of edges adjacent to the node.
  845. The weighted node degree is the sum of the edge weights for
  846. edges incident to that node.
  847. This object provides an iterator for (node, degree) as well as
  848. lookup for the degree for a single node.
  849. Parameters
  850. ----------
  851. nbunch : single node, container, or all nodes (default= all nodes)
  852. The view will only report edges incident to these nodes.
  853. weight : string or None, optional (default=None)
  854. The name of an edge attribute that holds the numerical value used
  855. as a weight. If None, then each edge has weight 1.
  856. The degree is the sum of the edge weights adjacent to the node.
  857. Returns
  858. -------
  859. DiDegreeView or int
  860. If multiple nodes are requested (the default), returns a `DiDegreeView`
  861. mapping nodes to their degree.
  862. If a single node is requested, returns the degree of the node as an integer.
  863. See Also
  864. --------
  865. in_degree, out_degree
  866. Examples
  867. --------
  868. >>> G = nx.DiGraph() # or MultiDiGraph
  869. >>> nx.add_path(G, [0, 1, 2, 3])
  870. >>> G.degree(0) # node 0 with degree 1
  871. 1
  872. >>> list(G.degree([0, 1, 2]))
  873. [(0, 1), (1, 2), (2, 2)]
  874. """
  875. return DiDegreeView(self)
  876. @cached_property
  877. def in_degree(self):
  878. """An InDegreeView for (node, in_degree) or in_degree for single node.
  879. The node in_degree is the number of edges pointing to the node.
  880. The weighted node degree is the sum of the edge weights for
  881. edges incident to that node.
  882. This object provides an iteration over (node, in_degree) as well as
  883. lookup for the degree for a single node.
  884. Parameters
  885. ----------
  886. nbunch : single node, container, or all nodes (default= all nodes)
  887. The view will only report edges incident to these nodes.
  888. weight : string or None, optional (default=None)
  889. The name of an edge attribute that holds the numerical value used
  890. as a weight. If None, then each edge has weight 1.
  891. The degree is the sum of the edge weights adjacent to the node.
  892. Returns
  893. -------
  894. If a single node is requested
  895. deg : int
  896. In-degree of the node
  897. OR if multiple nodes are requested
  898. nd_iter : iterator
  899. The iterator returns two-tuples of (node, in-degree).
  900. See Also
  901. --------
  902. degree, out_degree
  903. Examples
  904. --------
  905. >>> G = nx.DiGraph()
  906. >>> nx.add_path(G, [0, 1, 2, 3])
  907. >>> G.in_degree(0) # node 0 with degree 0
  908. 0
  909. >>> list(G.in_degree([0, 1, 2]))
  910. [(0, 0), (1, 1), (2, 1)]
  911. """
  912. return InDegreeView(self)
  913. @cached_property
  914. def out_degree(self):
  915. """An OutDegreeView for (node, out_degree)
  916. The node out_degree is the number of edges pointing out of the node.
  917. The weighted node degree is the sum of the edge weights for
  918. edges incident to that node.
  919. This object provides an iterator over (node, out_degree) as well as
  920. lookup for the degree for a single node.
  921. Parameters
  922. ----------
  923. nbunch : single node, container, or all nodes (default= all nodes)
  924. The view will only report edges incident to these nodes.
  925. weight : string or None, optional (default=None)
  926. The name of an edge attribute that holds the numerical value used
  927. as a weight. If None, then each edge has weight 1.
  928. The degree is the sum of the edge weights adjacent to the node.
  929. Returns
  930. -------
  931. If a single node is requested
  932. deg : int
  933. Out-degree of the node
  934. OR if multiple nodes are requested
  935. nd_iter : iterator
  936. The iterator returns two-tuples of (node, out-degree).
  937. See Also
  938. --------
  939. degree, in_degree
  940. Examples
  941. --------
  942. >>> G = nx.DiGraph()
  943. >>> nx.add_path(G, [0, 1, 2, 3])
  944. >>> G.out_degree(0) # node 0 with degree 1
  945. 1
  946. >>> list(G.out_degree([0, 1, 2]))
  947. [(0, 1), (1, 1), (2, 1)]
  948. """
  949. return OutDegreeView(self)
  950. def clear(self):
  951. """Remove all nodes and edges from the graph.
  952. This also removes the name, and all graph, node, and edge attributes.
  953. Examples
  954. --------
  955. >>> G = nx.path_graph(4) # or DiGraph, MultiGraph, MultiDiGraph, etc
  956. >>> G.clear()
  957. >>> list(G.nodes)
  958. []
  959. >>> list(G.edges)
  960. []
  961. """
  962. self._succ.clear()
  963. self._pred.clear()
  964. self._node.clear()
  965. self.graph.clear()
  966. def clear_edges(self):
  967. """Remove all edges from the graph without altering nodes.
  968. Examples
  969. --------
  970. >>> G = nx.path_graph(4) # or DiGraph, MultiGraph, MultiDiGraph, etc
  971. >>> G.clear_edges()
  972. >>> list(G.nodes)
  973. [0, 1, 2, 3]
  974. >>> list(G.edges)
  975. []
  976. """
  977. for predecessor_dict in self._pred.values():
  978. predecessor_dict.clear()
  979. for successor_dict in self._succ.values():
  980. successor_dict.clear()
  981. def is_multigraph(self):
  982. """Returns True if graph is a multigraph, False otherwise."""
  983. return False
  984. def is_directed(self):
  985. """Returns True if graph is directed, False otherwise."""
  986. return True
  987. def to_undirected(self, reciprocal=False, as_view=False):
  988. """Returns an undirected representation of the digraph.
  989. Parameters
  990. ----------
  991. reciprocal : bool (optional)
  992. If True only keep edges that appear in both directions
  993. in the original digraph.
  994. as_view : bool (optional, default=False)
  995. If True return an undirected view of the original directed graph.
  996. Returns
  997. -------
  998. G : Graph
  999. An undirected graph with the same name and nodes and
  1000. with edge (u, v, data) if either (u, v, data) or (v, u, data)
  1001. is in the digraph. If both edges exist in digraph and
  1002. their edge data is different, only one edge is created
  1003. with an arbitrary choice of which edge data to use.
  1004. You must check and correct for this manually if desired.
  1005. See Also
  1006. --------
  1007. Graph, copy, add_edge, add_edges_from
  1008. Notes
  1009. -----
  1010. If edges in both directions (u, v) and (v, u) exist in the
  1011. graph, attributes for the new undirected edge will be a combination of
  1012. the attributes of the directed edges. The edge data is updated
  1013. in the (arbitrary) order that the edges are encountered. For
  1014. more customized control of the edge attributes use add_edge().
  1015. This returns a "deepcopy" of the edge, node, and
  1016. graph attributes which attempts to completely copy
  1017. all of the data and references.
  1018. This is in contrast to the similar G=DiGraph(D) which returns a
  1019. shallow copy of the data.
  1020. See the Python copy module for more information on shallow
  1021. and deep copies, https://docs.python.org/3/library/copy.html.
  1022. Warning: If you have subclassed DiGraph to use dict-like objects
  1023. in the data structure, those changes do not transfer to the
  1024. Graph created by this method.
  1025. Examples
  1026. --------
  1027. >>> G = nx.path_graph(2) # or MultiGraph, etc
  1028. >>> H = G.to_directed()
  1029. >>> list(H.edges)
  1030. [(0, 1), (1, 0)]
  1031. >>> G2 = H.to_undirected()
  1032. >>> list(G2.edges)
  1033. [(0, 1)]
  1034. """
  1035. graph_class = self.to_undirected_class()
  1036. if as_view is True:
  1037. return nx.graphviews.generic_graph_view(self, graph_class)
  1038. # deepcopy when not a view
  1039. G = graph_class()
  1040. G.graph.update(deepcopy(self.graph))
  1041. G.add_nodes_from((n, deepcopy(d)) for n, d in self._node.items())
  1042. if reciprocal is True:
  1043. G.add_edges_from(
  1044. (u, v, deepcopy(d))
  1045. for u, nbrs in self._adj.items()
  1046. for v, d in nbrs.items()
  1047. if v in self._pred[u]
  1048. )
  1049. else:
  1050. G.add_edges_from(
  1051. (u, v, deepcopy(d))
  1052. for u, nbrs in self._adj.items()
  1053. for v, d in nbrs.items()
  1054. )
  1055. return G
  1056. def reverse(self, copy=True):
  1057. """Returns the reverse of the graph.
  1058. The reverse is a graph with the same nodes and edges
  1059. but with the directions of the edges reversed.
  1060. Parameters
  1061. ----------
  1062. copy : bool optional (default=True)
  1063. If True, return a new DiGraph holding the reversed edges.
  1064. If False, the reverse graph is created using a view of
  1065. the original graph.
  1066. """
  1067. if copy:
  1068. H = self.__class__()
  1069. H.graph.update(deepcopy(self.graph))
  1070. H.add_nodes_from((n, deepcopy(d)) for n, d in self.nodes.items())
  1071. H.add_edges_from((v, u, deepcopy(d)) for u, v, d in self.edges(data=True))
  1072. return H
  1073. return nx.graphviews.reverse_view(self)