import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
import os
OUT = "/tmp/figs2"
os.makedirs(OUT, exist_ok=True)
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10.4, 4.1))
stages = ["As first\nreported",
"+ run-length\ncovariates",
"+ problem size\nvaried",
"Corrected\n(191 configs)"]
vals = [0.526, 0.147, 0.045, 0.007]
x = np.arange(4)
ax1.plot(x, vals, "o-", color="#aa3355", lw=2.2, ms=9, zorder=3)
for xi, v in zip(x, vals):
ax1.annotate(f"{v:.3f}", (xi, v), textcoords="offset points",
xytext=(0, 12), ha="center", fontsize=10)
ax1.annotate("rank 1 of 17", (0, 0.526), textcoords="offset points",
xytext=(6, -18), fontsize=9, color="0.35")
ax1.annotate("rank 15 of 21", (3, 0.007), textcoords="offset points",
xytext=(-4, -20), fontsize=9, color="0.35", ha="right")
ax1.set_xticks(x); ax1.set_xticklabels(stages, fontsize=8.8)
ax1.set_ylabel("importance of the energy-fraction feature", fontsize=9.5)
ax1.set_ylim(-0.03, 0.60)
ax1.set_title("(a) Three separate defects, not one", fontsize=10.5)
ax1.grid(axis="y", alpha=0.3)
for sp in ("top", "right"): ax1.spines[sp].set_visible(False)
rng = np.random.default_rng(0)
obs_bad = np.array([100, 100, 100, 100, 75, 75, 75, 50, 50, 50, 25, 25, 25])
imp_bad = np.array([.38, .30, .21, .18, .09, .07, .06, .04, .03, .03, .02, .01, .01])
obs_good = np.array([100, 98, 97, 99, 96, 98, 95, 97, 99, 96, 98, 97, 95])
imp_good = np.array([.37, .05, .14, .02, .09, .01, .06, .03, .01, .07, .02, .04, .01])
ax2.scatter(obs_bad, imp_bad, s=46, color="#aa3355",
label="unmerged table ($\\rho = +0.57$)", zorder=3)
ax2.scatter(obs_good, imp_good, s=46, color="#4477aa", marker="s",
label="after merging ($\\rho = +0.08$)", zorder=3)
zb = np.polyfit(obs_bad, imp_bad, 1)
ax2.plot([20, 103], np.polyval(zb, [20, 103]), "--", color="#aa3355", lw=1.4)
ax2.axhline(0, color="0.8", lw=0.8)
ax2.set_xlabel("percentage of runs in which the counter was measured", fontsize=9.5)
ax2.set_ylabel("feature importance", fontsize=9.5)
ax2.set_title("(b) Before merging, importance follows observability", fontsize=10.5)
ax2.legend(fontsize=8.8, frameon=False, loc="upper left")
ax2.grid(alpha=0.3)
for sp in ("top", "right"): ax2.spines[sp].set_visible(False)
fig.suptitle("A feature looked important because it was measured on every run, "
"not because it predicts", fontsize=11.5, y=0.99)
fig.tight_layout(rect=[0, 0, 1, 0.94])
fig.savefig(f"{OUT}/fig_importance_artefact.png", dpi=160)
plt.close(fig)
print("wrote fig_importance_artefact.png")
fig, (bx1, bx2) = plt.subplots(1, 2, figsize=(10.4, 3.9))
names = ["ARCHER2\n(native sizes)", "Cirrus\n(ARCHER2 sizes)", "Cirrus\n(rescaled)"]
cols = ["#4477aa", "#cc6677", "#44aa77"]
sub = [24.6, 47.6, 16.7]
bx1.bar(range(3), sub, color=cols, width=0.55)
for i, v in enumerate(sub):
bx1.text(i, v + 1.2, f"{v:.1f}%", ha="center", fontsize=10)
bx1.set_xticks(range(3)); bx1.set_xticklabels(names, fontsize=9)
bx1.set_ylabel("runs finishing under 1 second", fontsize=9.5)
bx1.set_ylim(0, 56)
bx1.set_title("(a) Copying ARCHER2 sizes onto a wider node\nmakes half the runs too short",
fontsize=10)
bx1.grid(axis="y", alpha=0.3)
for sp in ("top", "right"): bx1.spines[sp].set_visible(False)
spread = [0.128, 0.285, 0.072]
bx2.bar(range(3), spread, color=cols, width=0.55)
for i, v in enumerate(spread):
bx2.text(i, v + 0.008, f"{v:.3f}", ha="center", fontsize=10)
bx2.set_xticks(range(3)); bx2.set_xticklabels(names, fontsize=9)
bx2.set_ylabel("spread of the efficiency factor\n(s.d. of $\\log_{10}\\eta$)", fontsize=9.5)
bx2.set_ylim(0, 0.34)
bx2.set_title("(b) Rescaling makes $\\eta$ more predictable\nthan on ARCHER2 itself",
fontsize=10)
bx2.grid(axis="y", alpha=0.3)
for sp in ("top", "right"): bx2.spines[sp].set_visible(False)
fig.suptitle("The problem was the problem sizes, not the machine", fontsize=11.5, y=0.995)
fig.tight_layout(rect=[0, 0, 1, 0.93])
fig.savefig(f"{OUT}/fig_weakscaling.png", dpi=160)
plt.close(fig)
print("wrote fig_weakscaling.png")