codex/categorical-product-factorization.py

categorical-product-factorization.py
"""Explicit pairing map and projection equations; write the PNG to cwd."""

from pathlib import Path

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt


def main():
    source = ["u", "v", "w"]
    first = {"u": 0, "v": 1, "w": 1}
    second = {"u": "a", "v": "b", "w": "a"}
    pairs = [(a, b) for a in [0, 1] for b in ["a", "b", "c"]]
    pairing = {x: (first[x], second[x]) for x in source}
    assert all(pairing[x][0] == first[x] and pairing[x][1] == second[x]
               for x in source)
    colors = ["#176997", "#b95f22", "#43804d"]
    fig, axes = plt.subplots(1, 2, figsize=(10.4, 5.2), dpi=120,
                             gridspec_kw={"width_ratios": [1.1, 1]})
    ax = axes[0]
    pair_positions = {pair: 5 - i for i, pair in enumerate(pairs)}
    for i, x in enumerate(source):
        y = 4 - 1.5 * i
        ax.text(0, y, x, ha="center", va="center", fontsize=15,
                bbox={"boxstyle": "circle", "fc": "white", "ec": colors[i]})
        ax.annotate("", xy=(1.9, pair_positions[pairing[x]]), xytext=(0.13, y),
                    arrowprops={"arrowstyle": "-|>", "color": colors[i],
                                "lw": 2, "mutation_scale": 16})
    for pair, y in pair_positions.items():
        used = pair in pairing.values()
        ax.text(2.15, y, f"({pair[0]}, {pair[1]})", ha="center", va="center",
                fontsize=13, color="#222222" if used else "#879099",
                bbox={"boxstyle": "round,pad=0.3", "fc": "#f1f5f8",
                      "ec": "#b6c1cc"})
    ax.text(0, 5.75, "X", ha="center", fontsize=14)
    ax.text(2.15, 5.75, r"$A\times B$", ha="center", fontsize=14)
    ax.text(1.05, 5.05, "unique h", ha="center", fontsize=12)
    ax.set_xlim(-0.4, 2.7)
    ax.set_ylim(-0.65, 6.2)
    ax.axis("off")
    ax.set_title("Pair the two specified outputs", fontsize=13, pad=14)
    ax = axes[1]
    positions = {"X": (0.5, 1), "P": (0.5, 0.45),
                 "A": (0.06, 0.05), "B": (0.94, 0.05)}
    for name, position in positions.items():
        label = r"$P=A\times B$" if name == "P" else name
        ax.text(*position, label, ha="center", va="center", fontsize=14,
                bbox={"boxstyle": "round,pad=0.3", "fc": "#f1f5f8",
                      "ec": "#b6c1cc"}, zorder=5)
    for start, end, label, point, color in [
        ("X", "P", "h", (0.59, 0.72), "#43804d"),
        ("X", "A", "f", (0.17, 0.62), "#176997"),
        ("X", "B", "g", (0.83, 0.62), "#b95f22"),
        ("P", "A", r"$p_A$", (0.23, 0.22), "#176997"),
        ("P", "B", r"$p_B$", (0.77, 0.22), "#b95f22"),
    ]:
        ax.annotate("", xy=positions[end], xytext=positions[start],
                    arrowprops={"arrowstyle": "-|>", "color": color, "lw": 2,
                                "shrinkA": 22, "shrinkB": 22,
                                "mutation_scale": 18})
        ax.text(*point, label, fontsize=13, ha="center", color=color,
                bbox={"fc": "white", "ec": "none", "pad": 1})
    ax.text(0.5, -0.21, r"$p_A\circ h=f$     $p_B\circ h=g$",
            fontsize=14, ha="center")
    ax.set_xlim(-0.15, 1.15)
    ax.set_ylim(-0.32, 1.22)
    ax.axis("off")
    ax.set_title("Both projection equations must hold", fontsize=13, pad=14)
    fig.suptitle("A product combines maps without extra choices", fontsize=16)
    fig.subplots_adjust(top=0.8, bottom=0.08, left=0.025, right=0.975, wspace=0.15)
    fig.savefig(Path.cwd() / Path(__file__).with_suffix(".png").name,
                facecolor="white")
    plt.close(fig)


if __name__ == "__main__":
    main()