codexphoton-recoil-joint-lag-correlation.py
photon-recoil-joint-lag-correlation.py
"""Chosen analytic lag model and separate exact Bernoulli comparison.
No historical data. Python 3.14, matplotlib 3.10.7, numpy 2.3.5.
Run from _media; output basename.png to the current directory.
"""
from pathlib import Path
from math import erf, sqrt
import matplotlib.pyplot as plt
import numpy as np
def main():
fig, (ax, joint) = plt.subplots(1, 2, figsize=(11, 5.5),
gridspec_kw={"width_ratios": [1.3, 1]})
fig.patch.set_facecolor("white")
fig.suptitle("Pairing adds information that the two singles totals cannot supply", fontsize=14)
lag = np.linspace(-8, 8, 1601) # milliseconds
baseline = np.ones_like(lag) # T r_e r_gamma / 1000 per ms
density = baseline + 100/np.sqrt(2*np.pi)*np.exp(-lag**2/2)
ax.plot(lag, density, color="#2574a9", lw=2, label="Correlated-pair expectation")
ax.plot(lag, baseline, color="#b55220", ls="--", lw=2, label="Independent-pair baseline")
ax.fill_between(lag, 0, density, where=np.abs(lag) <= 1, color="#2574a9", alpha=.2)
ax.axvspan(4, 6, color="#b55220", alpha=.13)
ax.annotate("", xy=(1, 33), xytext=(-1, 33), arrowprops=dict(arrowstyle="<->"))
ax.text(0, 29, "Full width w = 2 ms", ha="center", fontsize=10)
center = 2 + 100*erf(1/sqrt(2))
delayed = 2 + 50*(erf(6/sqrt(2))-erf(4/sqrt(2)))
ax.text(-7.7, 38, f"Central window: {center:.2f}\nDelayed window: {delayed:.3f}", fontsize=10)
ax.text(5, 6, "Delayed\nwindow", ha="center", fontsize=10, color="#85421c")
ax.set(xlabel=r"Lag $\ell=t_\gamma-t_e$ (ms)", ylabel="Expected pairs per ms", xlim=(-8, 8), ylim=(0, 46))
ax.legend(loc="upper right", fontsize=9, frameon=False)
ax.grid(alpha=.15)
joint.axis("off")
joint.set(xlim=(0, 1), ylim=(0, 1))
joint.text(.5, .95, "Separate ideal bin model (no timing spread)", ha="center", fontsize=11)
joint.text(.5, .86, "1,000,000 bins; each bin is 1 ms", ha="center", fontsize=10)
rows = [["Electron singles", "2,000", "2,000"],
["Photon singles", "500", "500"],
["Both in same bin", "1", "101"]]
table = joint.table(cellText=rows, colLabels=["Expected counts", "Independent", "Correlated"],
cellLoc="center", colWidths=[.48, .26, .26], bbox=[0, .35, 1, .42])
table.auto_set_font_size(False)
table.set_fontsize(10)
for (row, col), cell in table.get_celld().items():
cell.set_edgecolor("#bbbbbb")
if row == 0:
cell.set_facecolor("#eef5fa")
if row == 3:
cell.set_facecolor("#fff0dc")
joint.text(.5, .25, "Same marginals, different joint probability", ha="center", fontsize=11)
joint.text(.5, .13, "All numbers are chosen model expectations.\nNeither panel reproduces historical counts.", ha="center", fontsize=10)
fig.text(.5, .025, r"Lag model: $T=1000$ s, $r_e=2$ s$^{-1}$, $r_\gamma=0.5$ s$^{-1}$, $S=0.1$ s$^{-1}$, $\sigma=1$ ms", ha="center", fontsize=11)
fig.subplots_adjust(left=.075, right=.985, bottom=.15, top=.87, wspace=.26)
fig.savefig(Path(__file__).stem + ".png", dpi=120, facecolor="white")
plt.close(fig)
if __name__ == "__main__":
main()