codex/four-state-amplitude-amplification.py

four-state-amplitude-amplification.py
"""Draw four-state-amplitude-amplification.png in cwd; Python 3.14, NumPy, Matplotlib.

From the repository root: cd _media && python ../four-state-amplitude-amplification.py
"""

from pathlib import Path

import matplotlib

matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np


def main():
    h = np.array([[1, 1], [1, -1]]) / np.sqrt(2)
    uniform = np.kron(h, h) @ np.array([1, 0, 0, 0])
    oracle = np.diag([1, 1, 1, -1])
    flipped = oracle @ uniform
    diffusion = 2 * np.outer(uniform, uniform) - np.eye(4)
    amplified = diffusion @ flipped
    stages = np.array([uniform, flipped, amplified])
    assert np.allclose(uniform, [0.5] * 4)
    assert np.allclose(flipped, [0.5, 0.5, 0.5, -0.5])
    assert np.isclose(flipped.mean(), 0.25)
    assert np.allclose(amplified, [0, 0, 0, 1])
    assert np.allclose(amplified, 2 * flipped.mean() - flipped)
    assert np.allclose(np.sum(stages ** 2, axis=1), 1)
    assert np.allclose(diffusion.T @ diffusion, np.eye(4))
    # The formula must describe arbitrary input amplitudes, including complex ones.
    probe = np.array([1 + 2j, -3j, 2 - 1j, -0.25 + 0.5j])
    assert np.allclose(diffusion @ probe, 2 * probe.mean() - probe)
    for title, values in zip(["Prepared", "Phase flipped", "Amplified"], stages):
        print(f"{title}: amplitudes={values.round(12).tolist()}, "
              f"norm_squared={np.dot(values, values):.12f}, "
              f"marked_probability={values[3] ** 2:.12f}")
    print(f"Mean after oracle: {flipped.mean():.12f}")

    blue, orange = "#1765a8", "#bf5900"
    plt.rcParams.update({"font.size": 10, "axes.spines.top": False,
                         "axes.spines.right": False, "figure.facecolor": "white",
                         "axes.facecolor": "white"})
    fig, axes = plt.subplots(1, 3, figsize=(9.6, 4.1), sharey=True)
    fig.suptitle("Four states, one marked answer: one Grover iteration", fontsize=14, y=0.97)
    titles = [r"Prepare $H^{\otimes 2}|00\rangle$", "Flip the marked phase",
              r"Reflect: $a_x\mapsto 2\overline{a}-a_x$"]
    summaries = ["Each outcome has probability 1/4",
                 "Probabilities still all 1/4",
                 "Marked outcome has probability 1"]
    for ax, values, title, summary in zip(axes, stages, titles, summaries):
        visible = values.copy()
        visible[np.abs(visible) < 1e-14] = 0
        ax.bar(np.arange(4), visible, color=[blue, blue, blue, orange], width=0.64)
        ax.axhline(0, color="#4c5964", lw=0.9)
        ax.set_xticks(np.arange(4), [r"$|00\rangle$", r"$|01\rangle$",
                                   r"$|10\rangle$", r"$|11\rangle$"])
        ax.set(xlim=(-0.55, 3.55), ylim=(-0.73, 1.17), title=title)
        ax.grid(axis="y", alpha=0.15)
        ax.set_axisbelow(True)
        for i, value in enumerate(visible):
            ax.text(i, value + (0.05 if value >= 0 else -0.06), f"{value:g}",
                    ha="center", va="bottom" if value >= 0 else "top", fontsize=11)
        ax.text(0.5, -0.21, summary, transform=ax.transAxes, ha="center", fontsize=9)
    axes[0].set_ylabel("Real probability amplitude (signed)")
    axes[0].set_yticks([-0.5, 0, 0.25, 0.5, 1])
    axes[1].axhline(flipped.mean(), color="#727272", ls="--", lw=1.4)
    axes[1].text(-0.35, 0.29, r"Mean $\overline{a}=1/4$", color="#505050", fontsize=9,
                 bbox={"facecolor": "white", "edgecolor": "none", "pad": 1})
    fig.text(0.5, 0.035, r"Orange marks $|11\rangle$. The dashed mean is an amplitude, not a probability.",
             ha="center", fontsize=10)
    fig.subplots_adjust(left=0.075, right=0.985, top=0.78, bottom=0.27, wspace=0.2)
    path = Path.cwd() / (Path(__file__).stem + ".png")
    fig.savefig(path, dpi=120, facecolor="white", transparent=False)
    plt.close(fig)
    print(path)


if __name__ == "__main__":
    main()