codex/union-bound-failure-budget-sample-space.py

union-bound-failure-budget-sample-space.py
"""Draw finite failure-event budgets and their exact complements.

Run from _media. Tested with Python 3.14.4, matplotlib 3.10.7, numpy 2.3.5.
Every outcome has probability 1/20 in each of the two illustrative models.
"""

from fractions import Fraction
from pathlib import Path

import matplotlib.pyplot as plt
from matplotlib.patches import Patch, Rectangle
import numpy as np


def main():
    plt.rcParams.update({"font.size": 11, "font.family": "DejaVu Sans"})
    omega = set(range(1, 21))
    cases = [
        ("Separate failure outcomes", [{1}, {2}, {3}], Fraction(3, 20)),
        ("One shared failure outcome", [{1}, {1}, {1}], Fraction(1, 20)),
    ]
    colors = ["#276888", "#ba6421", "#8150a5"]
    fig, axes = plt.subplots(1, 2, figsize=(10.8, 6.8), facecolor="white")
    fig.subplots_adjust(left=0.04, right=0.97, bottom=0.10, top=0.80, wspace=0.18)
    fig.suptitle("Three failure allowances, one simultaneous-success guarantee", fontsize=15, y=0.97)
    fig.text(0.5, 0.915, "20 equally likely outcomes; each P(Fᵢ) = 1/20; total budget = 3/20", ha="center")
    fig.legend(handles=[Patch(facecolor=c, label=f"Failure F{i + 1}") for i, c in enumerate(colors)],
               loc="upper center", bbox_to_anchor=(0.5, 0.887), ncol=3, frameon=False)
    for ax, (title, events, expected) in zip(axes, cases, strict=True):
        union = set().union(*events)
        exact = Fraction(len(union), len(omega))
        budget = sum((Fraction(len(event), len(omega)) for event in events), Fraction())
        success = Fraction(len(omega - union), len(omega))
        assert exact == expected and budget == Fraction(3, 20)
        assert success == 1 - exact and success >= 1 - budget
        ax.set(xlim=(-0.65, 4.70), ylim=(-3.5, 4.2), aspect="equal")
        ax.axis("off")
        ax.set_title(title, fontsize=13, pad=6)
        ax.add_patch(Rectangle((-0.54, 0.12), 5.08, 3.98, facecolor="white", edgecolor="#8a919c", lw=1))
        for index, outcome in enumerate(sorted(omega)):
            x, y = index % 5, 3.55 - index // 5
            ax.add_patch(Rectangle((x - 0.39, y - 0.34), 0.78, 0.76,
                                   facecolor="#fff3e7" if outcome in union else "white",
                                   edgecolor="#586273", lw=0.9))
            ax.text(x, y + 0.14, str(outcome), ha="center", va="center", fontsize=11)
            for i, event in enumerate(events):
                ax.add_patch(Rectangle((x - 0.30 + 0.21 * i, y - 0.21), 0.17, 0.12,
                                       facecolor=colors[i] if outcome in event else "white",
                                       edgecolor=colors[i], lw=0.7))
        ax.text(2, -0.13, f"{len(union)} failure outcomes; {len(omega - union)} success outcomes", ha="center", fontsize=11)
        for y, value, label, color in [
            (-0.65, exact, "Exact any-failure probability", "#1b627f"),
            (-1.50, budget, "Union bound / total budget", "#a65b20"),
        ]:
            ax.text(-0.48, y + 0.28, f"{label}: {value}", fontsize=11)
            ax.add_patch(Rectangle((-0.48, y - 0.10), 4.94, 0.20, facecolor="#f0f1f3", edgecolor="none"))
            ax.add_patch(Rectangle((-0.48, y - 0.10), 4.94 * float(value), 0.20, facecolor=color, edgecolor="none"))
        for tick in np.array([0, 0.5, 1.0]):
            ax.plot([-0.48 + 4.94 * tick] * 2, [-1.70, -1.61], color="#586273", lw=0.8)
            ax.text(-0.48 + 4.94 * tick, -1.93, f"{tick:g}", ha="center", fontsize=9)
        ax.text(2, -2.55, f"Exact all-success probability: {success}", ha="center", fontsize=12)
        ax.text(2, -3.0, "Guaranteed all-success probability: at least 17/20", ha="center", fontsize=11)
    fig.text(0.5, 0.035, "Colored marks show event membership. The budget is valid under either pattern of dependence.",
             ha="center", fontsize=11)
    destination = Path.cwd() / (Path(__file__).stem + ".png")
    fig.savefig(destination, dpi=100, facecolor="white", transparent=False)
    plt.close(fig)
    print(destination)


if __name__ == "__main__":
    main()