codex/past-exam-of-the-mathematics-course-of-the-university-of-cambridge/2008/iii/paper-49-su3-weights.py

past-exam-of-the-mathematics-course-of-the-university-of-cambridge/2008/iii/paper-49-su3-weights.py
"""Draw exact SU(3) weights from interlacing patterns; write PNG to cwd."""
from collections import Counter
from pathlib import Path
import math
import matplotlib.pyplot as plt
import numpy as np

ROOTS = ((2, 0), (-1, 3), (1, 3))  # coordinates (2 H1, 2 sqrt(3) H2)

def weights(a, b):
    result = Counter()
    for upper in range(b, a + b + 1):
        for lower in range(b + 1):
            for bottom in range(lower, upper + 1):
                result[(2 * bottom - upper - lower,
                        3 * (upper + lower) - 2 * (a + 2 * b))] += 1
    assert sum(result.values()) == (a + 1) * (b + 1) * (a + b + 2) // 2
    return result

def xy(point):
    return np.array((point[0] / 2, point[1] / (2 * math.sqrt(3))))

def hull(points):
    points = sorted(points)
    def cross(o, a, b):
        return (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0])
    lower, upper = [], []
    for point in points:
        while len(lower) >= 2 and cross(lower[-2], lower[-1], point) <= 0:
            lower.pop()
        lower.append(point)
    for point in reversed(points):
        while len(upper) >= 2 and cross(upper[-2], upper[-1], point) <= 0:
            upper.pop()
        upper.append(point)
    return lower[:-1] + upper[:-1]

fig, axes = plt.subplots(2, 3, figsize=(12, 7.4), dpi=100, facecolor='white')
navy, muted = '#173c65', '#8598ac'
rootax = axes[0, 0]
for i, root in enumerate(ROOTS, 1):
    vector = xy(root)
    for sign in (-1, 1):
        rootax.annotate('', xy=sign * vector, xytext=(0, 0),
                        arrowprops={'arrowstyle': '-|>', 'color': navy if sign == 1 else muted,
                                    'lw': 1.7, 'mutation_scale': 15})
    rootax.text(*(1.18 * vector), rf'$\alpha_{i}$', ha='center', va='center', fontsize=14)
rootax.set(title='Three root directions', xlim=(-1.5, 1.5), ylim=(-1.22, 1.22))
rootax.text(0, -1.14, r'$\alpha_3=\alpha_1+\alpha_2$', fontsize=12, ha='center')

specs = [(1, 0, r'$\mathbf{3}$'), (0, 1, r'$\overline{\mathbf{3}}$'),
         (1, 1, r'$\mathbf{8}$'), (3, 0, r'$\mathbf{10}$'), (2, 2, r'$\mathbf{27}$')]
for ax, (a, b, label) in zip(axes.flat[1:], specs):
    data = weights(a, b)
    for point in data:
        for du, dv in ROOTS:
            neighbor = (point[0] + du, point[1] + dv)
            if neighbor in data:
                segment = np.array([xy(point), xy(neighbor)])
                ax.plot(segment[:, 0], segment[:, 1], color='#d7e0e9', lw=1.2, zorder=1)
    boundary = hull(data)
    path = np.array([xy(p) for p in boundary + boundary[:1]])
    ax.plot(path[:, 0], path[:, 1], color=muted, lw=1.4, zorder=2)
    for point, multiplicity in data.items():
        x, y = xy(point)
        ax.scatter([x], [y], s=65 if multiplicity == 1 else 290, color=navy, zorder=3)
        if multiplicity > 1:
            ax.text(x, y, str(multiplicity), color='white', ha='center', va='center',
                    fontsize=13, fontweight='bold', zorder=4)
    values = np.array([xy(p) for p in data])
    xmin, ymin = values.min(axis=0); xmax, ymax = values.max(axis=0)
    ax.set(xlim=(xmin - 0.38, xmax + 0.38), ylim=(ymin - 0.38, ymax + 0.38))
    ax.set_title(label + rf'    Dynkin labels $({a},{b})$', fontsize=14, pad=13)
for ax in axes.flat:
    ax.set_aspect('equal', adjustable='box')
    ax.spines[['top', 'right']].set_visible(False)
    ax.spines[['bottom', 'left']].set_color('#bcc7d2')
    ax.tick_params(labelsize=10, colors='#52677e')
    ax.set_xlabel(r'$H_1$ weight', fontsize=11)
    ax.set_ylabel(r'$H_2$ weight', fontsize=11)
fig.suptitle(r'$SU(3)$ root directions and weight multiplicities', fontsize=19, y=0.985)
fig.text(0.5, 0.018, 'Each plain dot has multiplicity 1; numbers mark coincident states. '
         'Lines follow the three root directions.', ha='center', fontsize=12, color=navy)
fig.subplots_adjust(left=0.07, right=0.98, top=0.87, bottom=0.11, wspace=0.4, hspace=0.48)
fig.savefig(Path(__file__).with_suffix('.png').name, facecolor='white', transparent=False)
plt.close(fig)