codex/chromatic-polynomial-deletion-contraction.py

chromatic-polynomial-deletion-contraction.py
"""Triangle deletion-contraction, with exact three-color counts; PNG to CWD."""
from pathlib import Path

import matplotlib.pyplot as plt


def draw(ax, positions, edges, highlight=None):
    for edge in edges:
        x1, y1 = positions[edge[0]]
        x2, y2 = positions[edge[1]]
        selected = edge == highlight
        ax.plot([x1, x2], [y1, y2],
                color="#ba3a3a" if selected else "#404040",
                lw=3 if selected else 2, zorder=1)
    for label, (x, y) in positions.items():
        ax.scatter(x, y, s=1600, facecolor="#e8f0f6",
                   edgecolor="#246a9b", linewidth=2, zorder=2)
        ax.text(x, y, label, ha="center", va="center", fontsize=13, zorder=3)
    ax.set(xlim=(-1.2, 1.2), ylim=(-1.05, 1.15), aspect="equal")
    ax.axis("off")


def main():
    fig, axes = plt.subplots(1, 3, figsize=(10.5, 4.5), dpi=120)
    fig.patch.set_facecolor("white")
    pos = {"A": (-0.75, 0.6), "B": (0, -0.6), "C": (0.75, 0.6)}
    draw(axes[0], pos, [("A", "B"), ("B", "C"), ("A", "C")], ("A", "C"))
    draw(axes[1], pos, [("A", "B"), ("B", "C")])
    draw(axes[2], {"AC": (0, 0.6), "B": (0, -0.6)}, [("AC", "B")])
    titles = ["G: triangle", "G − e: delete AC", "G / e: merge A and C"]
    counts = ["6 colourings", "12 colourings", "6 colourings"]
    for ax, title, count in zip(axes, titles, counts):
        ax.set_title(title, fontsize=12, pad=7)
        ax.text(0.5, -0.015, count, transform=ax.transAxes,
                ha="center", fontsize=13, weight="bold")
    fig.suptitle(r"$P_G(t)=P_{G-e}(t)-P_{G/e}(t)$", fontsize=18, y=0.97)
    fig.text(0.5, 0.035,
             "At t = 3: 6 = 12 − 6. Duplicate edges after contraction impose the same constraint.",
             ha="center", fontsize=11)
    fig.subplots_adjust(left=0.02, right=0.98, top=0.77, bottom=0.18, wspace=0.2)
    fig.savefig(Path.cwd() / Path(__file__).with_suffix(".png").name,
                facecolor="white", transparent=False)
    plt.close(fig)


if __name__ == "__main__":
    main()