digraph.py 48 KB

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