codex/past-exam-of-the-probability-statistics-and-options-ii-course-of-the-university-of-oxford/2007-tree-and-dual.py

past-exam-of-the-probability-statistics-and-options-ii-course-of-the-university-of-oxford/2007-tree-and-dual.py
#!/usr/bin/env python3
"""Python 3.14; matplotlib/numpy from the repository pyproject.toml. Output in CWD."""
from pathlib import Path
import numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon
fig,axs=plt.subplots(1,2,figsize=(11,5.3),facecolor='white',gridspec_kw={'width_ratios':[1,1.35]})
ax=axs[0];pos={'r':(0,0),'a':(-1,-1),'b':(1,-1),'c':(-1.6,-2),'1':(-.35,-2),'2':(-2,-3),'3':(-1.2,-3),'4':(.6,-2),'5':(1.4,-2)}
edges=[('r','a'),('r','b'),('a','c'),('a','1'),('c','2'),('c','3'),('b','4'),('b','5')]
blue={'a','b','2','3'}
for a,b in edges:ax.plot(*zip(pos[a],pos[b]),color='#303030',lw=1.7,zorder=1)
for name,(x,y) in pos.items():
 ax.scatter([x],[y],s=600,color='#c6e5f5' if name in blue else 'white',edgecolor='#222222',zorder=3)
 ax.text(x,y,name,ha='center',va='center',fontsize=12,zorder=4)
ax.set_title('Plane tree: 9 vertices, 8 edges',fontsize=13);ax.set_xlim(-2.55,2);ax.set_ylim(-3.5,.6);ax.set_aspect('equal');ax.axis('off')
ax=axs[1]
# Radial loops have a common endpoint O. Nested angular sectors encode the rooted tree.
loops=[('a',.10,3.04,4.3),('b',-3.04,-.10,4.3),('c',.27,1.80,3.0),('1',1.95,2.80,1.7),('2',.45,.98,1.1),('3',1.14,1.67,1.1),('4',-2.80,-1.85,2.0),('5',-1.55,-.35,2.4)]
for name,lo,hi,rad in loops:
 th=np.linspace(lo,hi,700);rr=rad*np.sin(np.pi*(th-lo)/(hi-lo));xy=np.c_[rr*np.cos(th),rr*np.sin(th)]
 ax.add_patch(Polygon(xy,closed=True,facecolor='#c6e5f5' if name in blue else 'white',edgecolor='#222222',lw=1.1))
 # Put the region label near its farthest point, leaving child sectors closer to O.
 mid=(lo+hi)/2;fac=.87 if name in ['a','b','c'] else .70
 ax.text(fac*rad*np.cos(mid),fac*rad*np.sin(mid),name,ha='center',va='center',fontsize=11)
ax.scatter([0],[0],s=45,color='#111111',zorder=10);ax.annotate('O',(0,0),xytext=(12,-6),textcoords='offset points',fontsize=11)
ax.text(-3.0,4.25,'r (outer face)',fontsize=10)
ax.set_title('Plane dual: 1 vertex, 8 loops, 9 faces',fontsize=13);ax.set_xlim(-3.2,3.2);ax.set_ylim(-4.7,4.7);ax.set_aspect('equal');ax.axis('off')
fig.suptitle('Two face colours follow the bipartition of the original tree',fontsize=14,y=.98)
fig.tight_layout(rect=(0,0,1,.94));fig.savefig(Path.cwd()/'2007-tree-and-dual.png',dpi=160,facecolor='white');plt.close(fig)