Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 136 additions & 0 deletions docs/source/edges_vertices.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,142 @@ For the directed case, each vertex maintains two edge lists: a list of outgoing
The graph class provides methods for adding and removing vertices and edges (see :doc:`modification`)
and for querying its structure (see :doc:`analysis`).

What a vertex holds
~~~~~~~~~~~~~~~~~~~~


Each vertex has **two** private edge lists, plus a back-pointer to its graph.

.. list-table::
:header-rows: 1
:widths: 15 55 30

* - Attribute
- For undirected graphs
- For directed graphs
* - ``_edgelist``
- the edges *incident on* it
- the edges *leaving* it
* - ``_inlist``
- empty
- the edges *entering* it
* - ``_graph``
- the graph it belongs to (``None`` if standalone)
- the graph it belongs to (``None`` if standalone)

Every list entry is a reference to the same :class:`~pgraph.PGraph.Edge` class object, and
there is exactly one edge object for each edge in the graph.

* **Undirected:** one list does everything, since an undirected edge is both "out"
and "in". That is also why a loop is stored twice, once for each end, and why it
counts 2 towards ``degree``.
* **Directed:** the two lists are the out-list and the in-list. A loop sits once in
each, so ``degree`` (in + out) is 2 there too.

An example, as lists
~~~~~~~~~~~~~~~~~~~~~~

For these simple graphs

.. plot::
:include-source: no
: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

import matplotlib.pyplot as plt
from matplotlib.patches import Circle, FancyArrowPatch

pos = {"a": (0.0, 0.0), "b": (2.0, 0.0), "c": (4.0, 0.0)}
# (name, from, to, curvature, label offset): e1 and e2 are parallel, drawn
# as two arcs curving to either side
edges = [("e1", "a", "b", 0.3, (0, 0.5)), ("e2", "a", "b", -0.3, (0, -0.5)),
("e3", "b", "c", 0, (0, 0.18))]

fig, axs = plt.subplots(1, 2, figsize=(9, 2.3))
for ax, is_directed, title in zip(axs, (False, True), ("UGraph", "DGraph")):
style = "-|>" if is_directed else "-"
for name, p, q, rad, (dx, dy) in edges:
ax.add_patch(FancyArrowPatch(pos[p], pos[q], arrowstyle=style, mutation_scale=16,
connectionstyle=f"arc3,rad={rad}", shrinkA=15, shrinkB=15,
color="0.15", lw=1.6))
mx = (pos[p][0] + pos[q][0]) / 2 + dx
my = (pos[p][1] + pos[q][1]) / 2 + dy
ax.text(mx, my, name, ha="center", va="center", fontsize=10, color="0.15")
x, y = pos["c"] # e4: a loop, an edge from c to itself
ax.add_patch(FancyArrowPatch((x - 0.15, y + 0.15), (x + 0.15, y + 0.15), arrowstyle=style,
mutation_scale=16, connectionstyle="arc3,rad=-1.8",
color="0.15", lw=1.6))
ax.text(x, y + 0.62, "e4", ha="center", va="center", fontsize=10, color="0.15")
for name, (x, y) in pos.items():
ax.add_patch(Circle((x, y), 0.2, fc="#1f77b4", ec="none", zorder=3))
ax.text(x, y, name, ha="center", va="center", color="white", fontsize=11, zorder=4)
ax.set_xlim(-0.6, 4.6)
ax.set_ylim(-0.8, 0.95)
ax.set_aspect("equal")
ax.axis("off")
ax.set_title(title, fontsize=11)
fig.tight_layout()

The vertices ``a``, ``b`` and ``c`` and edges ``e1`` and ``e2`` are added in that order.
The resulting vertex lists are

.. list-table::
:header-rows: 1
:widths: 10 30 30 30

* - Vertex name
- ``UGraph`` ``_edgelist``
- ``DGraph`` ``_edgelist``
- ``DGraph`` ``_inlist``
* - ``a``
- ``[e1, e2]``
- ``[e1, e2]``
- ``[]``
* - ``b``
- ``[e1, e2, e3]``
- ``[e3]``
- ``[e1, e2]``
* - ``c``
- ``[e3, e4, e4]``
- ``[e4]``
- ``[e3, e4]``

The graph also keeps **every edge once** in ``graph._edges`` (an insertion-ordered
dict, so ``[e1, e2, e3, e4]``).

The public view of those lists
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. list-table::
:header-rows: 1
:widths: 30 35 35

* - Method
- Returns for ``UGraph``
- Returns for ``DGraph``
* - ``edges()``, ``out_edges()``
- a copy of ``_edgelist``
- a copy of ``_edgelist``
* - ``in_edges()``
- a copy of ``_edgelist``
- a copy of ``_inlist``
* - ``degree``
- ``len(_edgelist)``
- ``len(_edgelist) + len(_inlist)``
* - ``in_degree``
- ``len(_edgelist)``
- ``len(_inlist)``
* - ``out_degree``
- ``len(_edgelist)``
- ``len(_edgelist)``
* - ``adjacent()``
- the far end of each edge in ``_edgelist``
- the far end of each edge in ``_edgelist``
* - ``incidences()``
- ``adjacent()`` paired with ``out_edges()``
- ``adjacent()`` paired with ``out_edges()``

All are returned in the order the edges were connected.


Working with vertices and edges directly
-----------------------------------------
Expand Down
Loading