codextriangle-permutation-operation-order.py
triangle-permutation-operation-order.py
"""Compare products in the triangle symmetry group; output PNG to CWD.
Tested with Python 3.14, matplotlib 3.10.7, and numpy 2.3.5.
"""
from pathlib import Path
import matplotlib.pyplot as plt
import numpy as np
def compose(p, q):
"""Apply q first, then p, to vertices indexed from zero."""
return tuple(p[q[i]] for i in range(3))
def draw_triangle(ax, permutation, title):
vertices = np.array([[0, 1], [-np.sqrt(3) / 2, -0.5],
[np.sqrt(3) / 2, -0.5]])
ax.plot(*vertices[[0, 1, 2, 0]].T, color="#536375", lw=2)
colors = ["#b43e39", "#1769a5", "#318445"]
for original, destination in enumerate(permutation):
x, y = vertices[destination]
ax.scatter(x, y, s=620, color=colors[original], zorder=3)
ax.text(x, y, str(original + 1), ha="center", va="center",
color="white", fontsize=17, weight="bold")
ax.set(xlim=(-1.2, 1.2), ylim=(-0.82, 1.3), aspect="equal")
ax.set_title(title, fontsize=15, pad=4)
ax.axis("off")
def main():
e, r, s = (0, 1, 2), (1, 2, 0), (1, 0, 2)
r2 = compose(r, r)
assert compose(r, r2) == compose(r2, r) == e
assert compose(r, s) == (2, 1, 0)
assert compose(s, r) == (0, 2, 1)
plt.rcParams.update({"font.family": "DejaVu Sans", "font.size": 12})
fig = plt.figure(figsize=(11, 7.2), facecolor="white")
fig.suptitle("Triangle symmetries: does operation order matter?", fontsize=19, y=.97)
fig.text(.5, .905, r"$r=(123)$: rotate counterclockwise 120° $s=(12)$: reflect, fixing position 3",
ha="center", fontsize=12)
panels = [(.055, .53, compose(r, r2), r"$rr^2=e$"),
(.335, .53, compose(r2, r), r"$r^2r=e$"),
(.055, .13, compose(r, s), r"$rs=(13)$"),
(.335, .13, compose(s, r), r"$sr=(23)$")]
for x, y, permutation, title in panels:
ax = fig.add_axes([x, y, .245, .275])
draw_triangle(ax, permutation, title)
fig.text(.32, .855, "Rotations commute: identical final labels", ha="center", color="#318445")
fig.text(.32, .455, "Rotation and reflection: different final labels", ha="center", color="#b43e39")
fig.text(.72, .79, r"$H=\langle r\rangle=\{e,r,r^2\}$", fontsize=15)
table_ax = fig.add_axes([.64, .34, .335, .39])
table_ax.axis("off")
rows = [["e", "yes", "yes"], ["(123)", "yes", "yes"],
["(132)", "yes", "yes"], ["(12)", "no", "yes"],
["(23)", "no", "yes"], ["(13)", "no", "yes"]]
table = table_ax.table(cellText=rows,
colLabels=["g", r"$g\in C_{S_3}(H)$", r"$g\in N_{S_3}(H)$"],
loc="center", cellLoc="center", colWidths=[.24, .38, .38])
table.auto_set_font_size(False)
table.set_fontsize(11)
table.scale(1, 1.8)
for (row, col), cell in table.get_celld().items():
cell.set_edgecolor("#c2cbd4")
cell.set_facecolor("#edf3f8" if row == 0 else "white")
fig.text(.805, .265, r"$srs^{-1}=r^{-1}\ne r$", ha="center", fontsize=16)
fig.text(.805, .205, "Reflection preserves H as a set,\nbut exchanges its two rotations.",
ha="center", fontsize=12, linespacing=1.6)
fig.text(.5, .045, "Colored labels track original vertices; the rightmost operation acts first.",
ha="center", fontsize=12)
fig.savefig(Path.cwd() / (Path(__file__).stem + ".png"), dpi=100,
facecolor="white", transparent=False)
plt.close(fig)
if __name__ == "__main__":
main()