codex/topological-minor-tree-contraction-degree-obstruction.py

topological-minor-tree-contraction-degree-obstruction.py
"""Draw a degree-three tree and its degree-four star contraction minor.

Tested with Python 3.14, matplotlib 3.10.7 and numpy 2.3.5.
Run from _media with MPLBACKEND=Agg; the matching PNG is saved to CWD.
"""

from pathlib import Path

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


TREE_EDGES = (("u", "v"), ("u", "a"), ("u", "b"), ("v", "c"), ("v", "d"))
CONTRACTION_MAP = {"u": "w", "v": "w", "a": "a", "b": "b", "c": "c", "d": "d"}


def vertex(ax, label, xy, centre=False):
    ax.add_patch(Circle(xy, 0.15, facecolor="white",
                        edgecolor="#2166ac" if centre else "#34495e",
                        linewidth=2.2, zorder=3))
    ax.text(*xy, label, ha="center", va="center", fontsize=14,
            weight="bold", color="#2166ac" if centre else "#34495e", zorder=4)


def main():
    plt.rcParams.update({"font.family": "DejaVu Sans", "font.size": 13})
    fig, axes = plt.subplots(1, 2, figsize=(9, 4.6), dpi=120, facecolor="white")
    fig.subplots_adjust(left=0.025, right=0.975, bottom=0.26, top=0.78, wspace=0.36)
    tree_pos = {"u": (-0.62, 0), "v": (0.62, 0), "a": (-1.55, 1),
                "b": (-1.55, -1), "c": (1.55, 1), "d": (1.55, -1)}
    star_pos = {"w": (0, 0), "a": (-1.25, 1), "b": (-1.25, -1),
                "c": (1.25, 1), "d": (1.25, -1)}
    star_edges = tuple(("w", leaf) for leaf in "abcd")
    axes[0].add_patch(Ellipse((0, 0), 1.8, 0.65, facecolor="#e8f1fa",
                             edgecolor="#2166ac", linestyle="--", linewidth=1.4))
    for ax, pos, edges in ((axes[0], tree_pos, TREE_EDGES), (axes[1], star_pos, star_edges)):
        for u, v in edges:
            ax.plot(*zip(pos[u], pos[v]), color="#2166ac" if (u, v) == ("u", "v") else "#34495e",
                    linewidth=3.3 if (u, v) == ("u", "v") else 2.2, zorder=1)
        for label, xy in pos.items():
            vertex(ax, label, xy, label in {"u", "v", "w"})
        ax.set(xlim=(-1.9, 1.9), ylim=(-1.3, 1.5), aspect="equal")
        ax.axis("off")
    axes[0].text(0, 0.72, "branch set {u, v}", ha="center", color="#2166ac", fontsize=12)
    axes[0].text(-0.62, -0.52, "degree 3", ha="center", fontsize=11)
    axes[0].text(0.62, -0.52, "degree 3", ha="center", fontsize=11)
    axes[1].text(0, -0.52, "degree 4", ha="center", fontsize=12, color="#2166ac")
    axes[0].set_title("Tree T: 6 vertices, 5 edges", fontsize=13, pad=12)
    axes[1].set_title(r"Contraction T/uv: star $K_{1,4}$", fontsize=13, pad=12)
    fig.suptitle("Contraction can combine the capacities of junctions", y=0.96,
                 fontsize=16, weight="bold")
    fig.add_artist(FancyArrowPatch((0.455, 0.52), (0.545, 0.52),
                                  transform=fig.transFigure, arrowstyle="-|>",
                                  mutation_scale=20, linewidth=1.7, color="#34495e"))
    fig.text(0.5, 0.34, "contract uv", ha="center", fontsize=12, color="#2166ac")
    fig.text(0.5, 0.17, r"$K_{1,4}$ is a minor of T, but not a topological minor.",
             ha="center", fontsize=14, weight="bold")
    fig.text(0.5, 0.085, "Four internally disjoint routes require four distinct edges leaving one centre.",
             ha="center", fontsize=12)
    fig.text(0.5, 0.035, "Every vertex of T has degree at most three.", ha="center", fontsize=12)
    output = Path.cwd() / (Path(__file__).stem + ".png")
    fig.savefig(output, facecolor="white", transparent=False)
    plt.close(fig)


if __name__ == "__main__":
    main()