codexpast-exam-of-the-mathematics-course-of-the-university-of-cambridge2007iiipaper-4-dynkin-diagrams.py
past-exam-of-the-mathematics-course-of-the-university-of-cambridge/2007/iii/paper-4-dynkin-diagrams.py
"""B-type Dynkin diagrams. Python 3.14, NumPy 2.3.5, Matplotlib 3.10.7.
Writes paper-4-dynkin-diagrams.png to caller CWD, preserving MPLCONFIGDIR.
"""
import os
import tempfile
os.environ.setdefault('MPLCONFIGDIR', tempfile.gettempdir() + '/codex-wiki-matplotlib')
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from matplotlib.patches import Circle
import numpy as np
def bond(ax, a, b, double=False, arrow=False, dashed=False):
a, b = np.array(a, float), np.array(b, float)
direction = (b-a)/np.linalg.norm(b-a)
side = np.array([-direction[1], direction[0]])
for offset in ([-.045, .045] if double else [0]):
ends = np.array([a+.13*direction+offset*side, b-.13*direction+offset*side])
ax.plot(ends[:,0], ends[:,1], color='black', lw=1.5, ls='--' if dashed else '-')
if arrow:
tip = a+.62*(b-a)
left = tip-.17*direction+.105*side
right = tip-.17*direction-.105*side
ax.plot([left[0], tip[0], right[0]], [left[1], tip[1], right[1]], color='black', lw=1.5)
def nodes(ax, positions):
for label, point in positions.items():
ax.add_patch(Circle(point, .13, edgecolor='black', facecolor='white', lw=1.5, zorder=3))
ax.text(point[0], point[1]-.31, '$'+label+'$', ha='center', va='top', fontsize=12)
def draw(ax, rank, affine):
if rank == 'general':
p = {'\\alpha_1':(0,0), '\\alpha_2':(1.1,0), '\\alpha_{n-1}':(3.1,0), '\\alpha_n':(4.2,0)}
bonds=[('\\alpha_1','\\alpha_2',False,False,False),('\\alpha_2','\\alpha_{n-1}',False,False,True),('\\alpha_{n-1}','\\alpha_n',True,True,False)]
ax.text(2.1,.08,r'$\cdots$',ha='center',fontsize=14)
title=r'$B_n$, $n\geq4$'
else:
p={f'\\alpha_{i}':(1.35*(i-1),0) for i in range(1,rank+1)}
bonds=[(f'\\alpha_{i}',f'\\alpha_{i+1}',i==rank-1, i==rank-1,False) for i in range(1,rank)]
title=f'$B_{rank}$' if rank>1 else r'$B_1=A_1$'
if affine:
if rank == 1:
p['\\alpha_0']=(-1.35,0)
bonds.append(('\\alpha_0','\\alpha_1',True,False,False))
elif rank == 2:
p={'\\alpha_0':(-1.35,0),'\\alpha_2':(0,0),'\\alpha_1':(1.35,0)}
bonds=[('\\alpha_0','\\alpha_2',True,True,False),('\\alpha_1','\\alpha_2',True,True,False)]
else:
p['\\alpha_0']=(p['\\alpha_1'][0],.95)
p['\\alpha_1']=(p['\\alpha_1'][0],-.8)
bonds.append(('\\alpha_0','\\alpha_2',False,False,False))
title+=': affine extension'
for a,b,double,arrow,dashed in bonds:bond(ax,p[a],p[b],double,arrow,dashed)
nodes(ax,p)
xs=[a[0] for a in p.values()]
ax.set_xlim(min(xs)-.5,max(xs)+.5)
ax.set_ylim(-1.4,1.5)
ax.set_aspect('equal')
ax.set_title(title,fontsize=12)
ax.axis('off')
def main():
fig,axes=plt.subplots(4,2,figsize=(10,8),constrained_layout=True)
for row,rank in enumerate(['general',3,2,1]):
draw(axes[row,0],rank,False)
draw(axes[row,1],rank,True)
fig.suptitle('Double bonds point toward short roots; rank-one roots have equal length',fontsize=13)
fig.savefig('paper-4-dynkin-diagrams.png',dpi=130,facecolor='white',transparent=False)
plt.close(fig)
if __name__=='__main__':main()