codexfano-error-entropy-budget.py
fano-error-entropy-budget.py
"""Plot the exact entropy budget for a symmetric four-symbol toy channel.
Save the PNG basename to cwd; run from _media with
MPLCONFIGDIR=/tmp/codex-wiki-matplotlib. Uses the repository's matplotlib/numpy
environment; tested with Python 3.14. No experimental data are used.
"""
from pathlib import Path
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
def binary_entropy(q):
q = np.asarray(q, dtype=float)
result = np.zeros_like(q)
mask = (q > 0) & (q < 1)
p = q[mask]
result[mask] = -p * np.log2(p) - (1 - p) * np.log2(1 - p)
return result
def main():
q = np.linspace(0, 1, 1001)
flag = binary_entropy(q)
alternatives = q * np.log2(3)
total = flag + alternatives
assert np.isclose(total[750], 2)
assert np.isclose(total[-1], np.log2(3))
assert np.all(np.diff(total[:751]) > 0)
assert np.all(np.diff(total[750:]) < 0)
plt.rcParams.update({"font.size": 12, "font.family": "DejaVu Sans"})
fig, ax = plt.subplots(figsize=(9.0, 5.7), facecolor="white")
fig.subplots_adjust(left=0.10, right=0.97, bottom=0.21, top=0.85)
ax.fill_between(q, 0, flag, color="#bfdcee", label=r"Error flag: $h_2(q)$")
ax.fill_between(q, flag, total, color="#f8d494",
label=r"Wrong-symbol uncertainty: $q\log_2 3$")
ax.plot(q, total, color="#163b61", linewidth=2.5,
label=r"Total: $H(X\mid Y)=h_2(q)+q\log_2 3$")
ax.plot(q, flag, color="#5790b4", linewidth=1.5)
ax.axvline(0.75, color="#516275", linestyle="--", linewidth=1.3)
for value in (0.3, 0.75, 1.0):
height = float(binary_entropy([value])[0] + value * np.log2(3))
ax.scatter([value], [height], color="#163b61", zorder=5, s=32)
ax.annotate("q = 0.3\n1.3568 bits", (0.3, float(total[300])), (0.07, 1.65),
arrowprops={"arrowstyle": "->", "color": "#516275"}, fontsize=11)
ax.annotate("q = 3/4: independent\n2 bits (maximum)", (0.75, 2), (0.49, 2.14),
arrowprops={"arrowstyle": "->", "color": "#516275"}, fontsize=11)
ax.annotate("q = 1: always wrong\n1.5850 bits remain", (1, float(total[-1])), (0.69, 1.17),
arrowprops={"arrowstyle": "->", "color": "#516275"}, fontsize=11,
bbox={"facecolor": "white", "edgecolor": "none", "alpha": 0.9, "pad": 3})
ax.set(xlim=(0, 1.01), ylim=(0, 2.42), xlabel="Error probability q of the identity decoder",
ylabel="Conditional entropy (bits)", xticks=[0, 0.25, 0.5, 0.75, 1])
ax.set_axisbelow(True)
ax.grid(alpha=0.2)
ax.legend(loc="lower left", fontsize=10, framealpha=0.95)
fig.suptitle("Fano's bound is exact for this four-symbol toy model", fontsize=15, y=0.97)
fig.text(0.5, 0.91, "Two uncertainties add; more raw errors need not mean more uncertainty",
ha="center", fontsize=11, color="#516275")
fig.text(0.5, 0.10,
"Identity decoder minimizes error for 0 ≤ q ≤ 3/4; for q > 3/4, guess another symbol.",
ha="center", fontsize=10)
fig.text(0.5, 0.045, "Uniform input; symmetric channel; theoretical probabilities, not measured data",
ha="center", fontsize=10, color="#516275")
fig.savefig(Path.cwd() / "fano-error-entropy-budget.png", dpi=120,
facecolor="white", transparent=False)
plt.close(fig)
if __name__ == "__main__":
main()