codexkl-bernoulli-penalty.py
kl-bernoulli-penalty.py
"""Plot directed Bernoulli penalties; tested with Python 3.14.4."""
from pathlib import Path
import matplotlib.pyplot as plt
import numpy as np
def bernoulli_kl(p, q):
return p * np.log(p / q) + (1 - p) * np.log((1 - p) / (1 - q))
def main():
p = 0.75
q = np.sort(np.concatenate((np.linspace(0.001, 0.999, 2000), [p])))
forward = bernoulli_kl(p, q)
reverse = bernoulli_kl(q, p)
plt.rcParams.update({"font.size": 11, "axes.spines.top": False,
"axes.spines.right": False})
fig, ax = plt.subplots(figsize=(8.4, 4.8), layout="constrained",
facecolor="white")
ax.plot(q, forward, color="#2368a2", lw=2.5,
label=r"$D(P\Vert Q)$: average under fixed $P$")
ax.plot(q, reverse, color="#bd6816", lw=2.2, ls="--",
label=r"$D(Q\Vert P)$: average under varying $Q$")
for predicted, offset in [(0.01, (16, -10)), (0.5, (-105, 32)),
(0.99, (-165, 50))]:
value = float(bernoulli_kl(p, predicted))
ax.scatter(predicted, value, color="#2368a2", s=38, zorder=4)
ax.annotate(f"q = {predicted:g}: {value:.6f} nat",
(predicted, value), xytext=offset,
textcoords="offset points", fontsize=10,
arrowprops={"arrowstyle": "-", "color": "#666666"})
print(f"q={predicted:g}: forward={value:.12f} nat, "
f"reverse={float(bernoulli_kl(predicted, p)):.12f} nat")
ax.scatter(p, 0, color="#222222", s=42, zorder=4)
ax.annotate("Both vanish at q = p = 0.75", (p, 0), xytext=(0.36, 1.05),
arrowprops={"arrowstyle": "->", "color": "#444444"})
ax.text(0.03, 4.1, r"$D(P\Vert Q)\to\infty$ at either endpoint",
color="#2368a2")
ax.text(0.03, 3.7, r"Reverse endpoints: $\log 4$ and $\log(4/3)$",
color="#bd6816")
ax.set(xlim=(0, 1), ylim=(-0.08, 4.6),
xticks=[0, 0.25, 0.5, 0.75, 1],
xlabel="Predicted success probability q",
ylabel="Relative entropy (nat)",
title="True success probability p = 0.75: confidence can be costly")
ax.grid(alpha=0.18)
ax.legend(loc="upper center", bbox_to_anchor=(0.57, 0.75),
framealpha=0.95, fontsize=10)
fig.savefig(Path.cwd() / "kl-bernoulli-penalty.png", dpi=120,
facecolor="white", transparent=False)
plt.close(fig)
if __name__ == "__main__":
main()