@@ -15,6 +15,142 @@ For the directed case, each vertex maintains two edge lists: a list of outgoing
1515The graph class provides methods for adding and removing vertices and edges (see :doc: `modification `)
1616and for querying its structure (see :doc: `analysis `).
1717
18+ What a vertex holds
19+ ~~~~~~~~~~~~~~~~~~~~
20+
21+
22+ Each vertex has **two ** private edge lists, plus a back-pointer to its graph.
23+
24+ .. list-table ::
25+ :header-rows: 1
26+ :widths: 15 55 30
27+
28+ * - Attribute
29+ - For undirected graphs
30+ - For directed graphs
31+ * - ``_edgelist ``
32+ - the edges *incident on * it
33+ - the edges *leaving * it
34+ * - ``_inlist ``
35+ - empty
36+ - the edges *entering * it
37+ * - ``_graph ``
38+ - the graph it belongs to (``None `` if standalone)
39+ - the graph it belongs to (``None `` if standalone)
40+
41+ Every list entry is a reference to the same :class: `~pgraph.PGraph.Edge ` class object, and
42+ there is exactly one edge object for each edge in the graph.
43+
44+ * **Undirected: ** one list does everything, since an undirected edge is both "out"
45+ and "in". That is also why a loop is stored twice, once for each end, and why it
46+ counts 2 towards ``degree ``.
47+ * **Directed: ** the two lists are the out-list and the in-list. A loop sits once in
48+ each, so ``degree `` (in + out) is 2 there too.
49+
50+ An example, as lists
51+ ~~~~~~~~~~~~~~~~~~~~~~
52+
53+ For these simple graphs
54+
55+ .. plot ::
56+ :include-source: no
57+ :alt: the example graph drawn as an undirected and as a directed graph: two parallel edges e1 and e2 from a to b, e3 from b to c and a loop e4 on c
58+
59+ import matplotlib.pyplot as plt
60+ from matplotlib.patches import Circle, FancyArrowPatch
61+
62+ pos = {"a": (0.0, 0.0), "b": (2.0, 0.0), "c": (4.0, 0.0)}
63+ # (name, from, to, curvature, label offset): e1 and e2 are parallel, drawn
64+ # as two arcs curving to either side
65+ edges = [("e1", "a", "b", 0.3, (0, 0.5)), ("e2", "a", "b", -0.3, (0, -0.5)),
66+ ("e3", "b", "c", 0, (0, 0.18))]
67+
68+ fig, axs = plt.subplots(1, 2, figsize=(9, 2.3))
69+ for ax, is_directed, title in zip(axs, (False, True), ("UGraph", "DGraph")):
70+ style = "-|>" if is_directed else "-"
71+ for name, p, q, rad, (dx, dy) in edges:
72+ ax.add_patch(FancyArrowPatch(pos[p], pos[q], arrowstyle=style, mutation_scale=16,
73+ connectionstyle=f"arc3,rad={rad}", shrinkA=15, shrinkB=15,
74+ color="0.15", lw=1.6))
75+ mx = (pos[p][0] + pos[q][0]) / 2 + dx
76+ my = (pos[p][1] + pos[q][1]) / 2 + dy
77+ ax.text(mx, my, name, ha="center", va="center", fontsize=10, color="0.15")
78+ x, y = pos["c"] # e4: a loop, an edge from c to itself
79+ ax.add_patch(FancyArrowPatch((x - 0.15, y + 0.15), (x + 0.15, y + 0.15), arrowstyle=style,
80+ mutation_scale=16, connectionstyle="arc3,rad=-1.8",
81+ color="0.15", lw=1.6))
82+ ax.text(x, y + 0.62, "e4", ha="center", va="center", fontsize=10, color="0.15")
83+ for name, (x, y) in pos.items():
84+ ax.add_patch(Circle((x, y), 0.2, fc="#1f77b4", ec="none", zorder=3))
85+ ax.text(x, y, name, ha="center", va="center", color="white", fontsize=11, zorder=4)
86+ ax.set_xlim(-0.6, 4.6)
87+ ax.set_ylim(-0.8, 0.95)
88+ ax.set_aspect("equal")
89+ ax.axis("off")
90+ ax.set_title(title, fontsize=11)
91+ fig.tight_layout()
92+
93+ The vertices ``a ``, ``b `` and ``c `` and edges ``e1 `` and ``e2 `` are added in that order.
94+ The resulting vertex lists are
95+
96+ .. list-table ::
97+ :header-rows: 1
98+ :widths: 10 30 30 30
99+
100+ * - Vertex name
101+ - ``UGraph `` ``_edgelist ``
102+ - ``DGraph `` ``_edgelist ``
103+ - ``DGraph `` ``_inlist ``
104+ * - ``a ``
105+ - ``[e1, e2] ``
106+ - ``[e1, e2] ``
107+ - ``[] ``
108+ * - ``b ``
109+ - ``[e1, e2, e3] ``
110+ - ``[e3] ``
111+ - ``[e1, e2] ``
112+ * - ``c ``
113+ - ``[e3, e4, e4] ``
114+ - ``[e4] ``
115+ - ``[e3, e4] ``
116+
117+ The graph also keeps **every edge once ** in ``graph._edges `` (an insertion-ordered
118+ dict, so ``[e1, e2, e3, e4] ``).
119+
120+ The public view of those lists
121+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
122+
123+ .. list-table ::
124+ :header-rows: 1
125+ :widths: 30 35 35
126+
127+ * - Method
128+ - Returns for ``UGraph ``
129+ - Returns for ``DGraph ``
130+ * - ``edges() ``, ``out_edges() ``
131+ - a copy of ``_edgelist ``
132+ - a copy of ``_edgelist ``
133+ * - ``in_edges() ``
134+ - a copy of ``_edgelist ``
135+ - a copy of ``_inlist ``
136+ * - ``degree ``
137+ - ``len(_edgelist) ``
138+ - ``len(_edgelist) + len(_inlist) ``
139+ * - ``in_degree ``
140+ - ``len(_edgelist) ``
141+ - ``len(_inlist) ``
142+ * - ``out_degree ``
143+ - ``len(_edgelist) ``
144+ - ``len(_edgelist) ``
145+ * - ``adjacent() ``
146+ - the far end of each edge in ``_edgelist ``
147+ - the far end of each edge in ``_edgelist ``
148+ * - ``incidences() ``
149+ - ``adjacent() `` paired with ``out_edges() ``
150+ - ``adjacent() `` paired with ``out_edges() ``
151+
152+ All are returned in the order the edges were connected.
153+
18154
19155Working with vertices and edges directly
20156-----------------------------------------
0 commit comments