"""Draw exact qubit precession for H = hbar * omega * sigma_z / 2. Tested with Python 3.14, matplotlib 3.10.7 and NumPy 2.3.5. Run from _media; writes unitary-qubit-precession.png to the current directory. """ from pathlib import Path import matplotlib.pyplot as plt import numpy as np from matplotlib.patches import Arc, FancyArrowPatch def main(): plt.rcParams.update({"font.size": 11, "axes.spines.top": False, "axes.spines.right": False, "figure.facecolor": "white", "axes.facecolor": "white", "savefig.facecolor": "white"}) fig, (equator, probabilities) = plt.subplots( 1, 2, figsize=(10.8, 4.8), gridspec_kw={"width_ratios": [1, 1.35]} ) fig.suptitle("Unitary qubit precession: phase changes, energy populations do not", fontsize=14, y=0.96) angles = np.linspace(0, 2 * np.pi, 401) equator.plot(np.cos(angles), np.sin(angles), color="#a1adb8", lw=2) equator.axhline(0, color="#dae0e5", lw=1) equator.axvline(0, color="#dae0e5", lw=1) equator.add_patch(Arc((0, 0), 1.5, 1.5, theta1=10, theta2=72, color="#b66c10", lw=2)) equator.add_patch(FancyArrowPatch((0.26, 0.70), (0.19, 0.73), arrowstyle="-|>", mutation_scale=14, color="#b66c10", lw=2)) phase = np.pi / 3 tip = np.array([np.cos(phase), np.sin(phase)]) equator.annotate("", xy=tip, xytext=(0, 0), arrowprops={"arrowstyle": "-|>", "lw": 2.5, "color": "#b66c10"}) equator.text(0.25, 0.22, r"$\omega t$", color="#9a5a08", fontsize=12) equator.text(0.65, 0.92, r"$\mathbf{r}(t)$", color="#9a5a08") equator.plot([1, 0, -1, 0], [0, 1, 0, -1], "o", color="#334b63", ms=5) equator.text(1.05, 0.04, "$+x$\n" + r"$|+\rangle$", ha="left", va="center") equator.text(-1.05, 0.04, "$-x$\n" + r"$|-\rangle$", ha="right", va="center") equator.text(0, 1.08, r"$+y$ : $|+i\rangle$", ha="center", va="bottom") equator.text(0, -1.08, r"$-y$ : $|-i\rangle$", ha="center", va="top") equator.set(xlim=(-1.5, 1.5), ylim=(-1.38, 1.42), aspect="equal") equator.set_title("Bloch sphere equator\nviewed from $+z$", fontsize=12, pad=10) equator.axis("off") probabilities.plot(angles, (1 + np.cos(angles)) / 2, color="#7346a5", lw=2.3, label=r"$P_X(+)=(1+\cos\omega t)/2$") probabilities.plot(angles, (1 + np.sin(angles)) / 2, color="#147c96", lw=2.3, label=r"$P_Y(+)=(1+\sin\omega t)/2$") probabilities.axhline(0.5, color="#4c5864", lw=1.8, ls="--", label=r"$P_Z(0)=P_Z(1)=1/2$") probabilities.set(xlim=(0, 2 * np.pi), ylim=(-0.035, 1.035), xlabel=r"Accumulated relative phase $\omega t$", ylabel="Probability") probabilities.set_xticks([0, np.pi / 2, np.pi, 3 * np.pi / 2, 2 * np.pi], ["0", r"$\pi/2$", r"$\pi$", r"$3\pi/2$", r"$2\pi$"]) probabilities.set_yticks([0, 0.25, 0.5, 0.75, 1]) probabilities.grid(alpha=0.18) probabilities.legend(loc="upper center", bbox_to_anchor=(0.5, -0.22), fontsize=10, frameon=False, ncol=1) fig.text(0.25, 0.06, r"At $\omega t=2\pi$: $U=-I$, same physical state.", ha="center", fontsize=10) fig.text(0.5, 0.015, "Ideal model • exact probabilities • separate measurement settings", ha="center", color="#4c5864", fontsize=10) fig.subplots_adjust(left=0.055, right=0.98, top=0.76, bottom=0.31, wspace=0.26) fig.savefig(Path.cwd() / (Path(__file__).stem + ".png"), dpi=100, transparent=False) plt.close(fig) if __name__ == "__main__": main()