codex/past-exam-of-the-graph-theory-and-probabilistic-combinatorics-course-of-the-university-of-oxford/2010-dyadic-colouring.py

past-exam-of-the-graph-theory-and-probabilistic-combinatorics-course-of-the-university-of-oxford/2010-dyadic-colouring.py
#!/usr/bin/env python3
"""Alternating dyadic colouring; Python 3.14, NumPy and Matplotlib.
Outputs a white-background PNG in the working directory.
"""
from pathlib import Path
import numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
fig,ax=plt.subplots(figsize=(8,3.8),layout='constrained',facecolor='white')
ax.set_facecolor('white')
colors=['#376eaa','#d98532']
for j in range(7):
 ax.add_patch(Rectangle((j,.1),1,.32,color=colors[j%2],alpha=.85))
 ax.text(j+.5,.26,r'$C_'+str(j%2)+'$',ha='center',va='center',color='white',fontsize=12)
for row,x in enumerate([3,5,9,17]):
 lx=np.log2(x); y=-.16-.24*row
 ax.annotate('',xy=(lx+1,y),xytext=(lx,y),arrowprops={'arrowstyle':'->','lw':1.6,'color':'#333333'})
 ax.scatter([lx,lx+1],[y,y],c=[colors[int(lx)%2],colors[int(lx+1)%2]],s=27,zorder=4)
 ax.text(lx-.08,y,str(x),ha='right',va='center',fontsize=10)
 ax.text(lx+1.08,y,str(2*x),ha='left',va='center',fontsize=10)
ax.set_xticks(range(8),[str(2**j)for j in range(8)])
ax.xaxis.tick_top()
ax.set(xlim=(-.1,7.1),ylim=(-1.05,.65),yticks=[],title='Alternating dyadic blocks: doubling always changes colour')
ax.text(3.5,-1.02,r'Addition-closed $S$ contains $x$ and $2x$, so it cannot be monochromatic.',ha='center',va='bottom',fontsize=11)
for spine in ax.spines.values():spine.set_visible(False)
fig.savefig(Path.cwd()/'2010-dyadic-colouring.png',dpi=160,facecolor='white')