codexrational-addition-commuting-counterexample.py
rational-addition-commuting-counterexample.py
"""Draw correct addition and an invariant-preserving incorrect operation.
Tested with Python 3.14, matplotlib 3.10.7, numpy 2.3.5 (root pyproject.toml).
Run from _media; writes the matching PNG to the current directory.
"""
from fractions import Fraction
from pathlib import Path
import matplotlib.pyplot as plt
from matplotlib.patches import FancyArrowPatch, FancyBboxPatch
def main():
plt.rcParams.update({"font.family": "DejaVu Sans", "font.size": 12})
fig, axes = plt.subplots(1, 2, figsize=(12, 5.8), dpi=120, facecolor="white")
blue, green, red = "#245b93", "#24725b", "#a63535"
expected = Fraction(1, 2) + Fraction(1, 3)
assert expected == Fraction(5, 6)
cases = [
("Correct addition", "F", (5, 6), green, True),
("Incorrect operation", "B", (2, 6), red, False),
]
for ax, (title, operation, result, color, correct) in zip(axes, cases):
ax.set(xlim=(0, 6), ylim=(0, 6))
ax.axis("off")
def node(x, y, label, width=1.8, height=0.72, tint=blue):
ax.add_patch(FancyBboxPatch(
(x - width / 2, y - height / 2), width, height,
boxstyle="round,pad=0.04,rounding_size=0.10",
edgecolor=tint, facecolor="white", linewidth=1.5,
))
ax.text(x, y, label, ha="center", va="center", fontsize=14, color=tint)
def arrow(start, end, label, text_xy, tint=blue):
ax.add_patch(FancyArrowPatch(
start, end, arrowstyle="-|>", mutation_scale=15,
color=tint, linewidth=1.7,
))
ax.text(*text_xy, label, ha="center", va="center", fontsize=13, color=tint)
observed = Fraction(*result)
assert (observed == expected) == correct
assert result[1] != 0
ax.text(3, 5.62, title, ha="center", fontsize=17, weight="bold", color=color)
formula = (r"$F((a,b),(c,d))=(ad+bc,bd)$" if correct
else r"$B((a,b),(c,d))=(a+c,bd)$")
ax.text(3, 5.10, formula, ha="center", fontsize=13, color=color)
node(1.08, 4.12, "(1, 2)\n(1, 3)", height=0.94)
node(4.80, 4.12, f"{result}", tint=color)
arrow((2.05, 4.12), (3.82, 4.12), operation, (3.00, 4.39), color)
node(1.08, 1.54, r"$(\frac{1}{2},\frac{1}{3})$", height=0.9, tint=green)
node(4.80, 1.54, r"$\frac{5}{6}$", width=1.0, tint=green)
arrow((1.08, 3.57), (1.08, 2.07), r"$\alpha\times\alpha$", (0.64, 2.83))
arrow((2.05, 1.54), (4.19, 1.54), "abstract addition", (3.10, 1.12), green)
if correct:
arrow((4.80, 3.70), (4.80, 2.03), r"$\alpha$", (5.20, 2.85), green)
ax.text(3, 0.48, "Both paths give 5/6", ha="center", color=green, fontsize=14)
else:
node(4.80, 2.85, r"$\frac{1}{3}$", width=1.0, tint=red)
arrow((4.80, 3.70), (4.80, 3.20), r"$\alpha$", (5.35, 3.46), red)
ax.text(4.80, 2.15, r"$\ne$", ha="center", va="center", color=red, fontsize=24)
ax.text(3, 0.48, "Valid output; wrong abstract value", ha="center", color=red, fontsize=13)
fig.suptitle("A nonzero denominator does not prove correct addition",
fontsize=18, weight="bold", y=0.99)
fig.subplots_adjust(left=0.015, right=0.985, top=0.90, bottom=0.04, wspace=0.06)
fig.savefig(Path(__file__).with_suffix(".png").name,
facecolor="white", transparent=False)
plt.close(fig)
if __name__ == "__main__":
main()