|
| 1 | +"""Chapter 1, Sec. 1.4: three ways of loading a classical data vector into |
| 2 | +a qubit register, Eqs. (1-basis), (1-angle) and (1-amplitude). |
| 3 | +
|
| 4 | + basis encoding an integer (or bit string) becomes a basis state |
| 5 | + angle encoding one feature per qubit, as a rotation angle |
| 6 | + amplitude encoding the normalized vector becomes the amplitudes |
| 7 | +
|
| 8 | +The script prints the three encodings of the same small vector, counts the |
| 9 | +qubits each needs for d features (Fig. 1-qubitcount) and shows why angle |
| 10 | +encoding needs its inputs rescaled: the encoded state is periodic in the |
| 11 | +feature (Fig. 1-periodic). Only the shared state-vector simulator of |
| 12 | +common/ is used. |
| 13 | +""" |
| 14 | +import sys, pathlib |
| 15 | +root = pathlib.Path(__file__).resolve().parents[1] # BookPrograms/ |
| 16 | +sys.path[:0] = [str(p) for p in root.iterdir() if p.is_dir()] |
| 17 | + |
| 18 | +import numpy as np |
| 19 | + |
| 20 | +from statevector import StateVector |
| 21 | + |
| 22 | + |
| 23 | +def basis_encoding(k, n_qubits): |
| 24 | + """|k> for the integer k written with n_qubits bits, Eq. (1-basis).""" |
| 25 | + return StateVector.from_bits(format(k, f"0{n_qubits}b")) |
| 26 | + |
| 27 | + |
| 28 | +def angle_encoding(x, rotation="Y"): |
| 29 | + """(x) R(x_i)|0>: one rotation per feature, Eq. (1-angle).""" |
| 30 | + sv = StateVector(len(x)) |
| 31 | + for q, xq in enumerate(x): |
| 32 | + if rotation == "Y": |
| 33 | + sv.ry(xq, q) |
| 34 | + elif rotation == "X": |
| 35 | + sv.rx(xq, q) |
| 36 | + else: |
| 37 | + sv.h(q); sv.rz(xq, q) |
| 38 | + return sv |
| 39 | + |
| 40 | + |
| 41 | +def amplitude_encoding(x): |
| 42 | + """sum_k x_k |k> / ||x||, padded with zeros to 2^n amplitudes, Eq. (1-amplitude).""" |
| 43 | + n = int(np.ceil(np.log2(len(x)))) if len(x) > 1 else 1 |
| 44 | + amp = np.zeros(2 ** n) |
| 45 | + amp[:len(x)] = x |
| 46 | + return StateVector(n, amp) |
| 47 | + |
| 48 | + |
| 49 | +def qubits_needed(d, precision_bits=8): |
| 50 | + """Number of qubits for d features in the three encodings.""" |
| 51 | + return {"basis (8 bits per feature)": precision_bits * d, |
| 52 | + "angle": d, |
| 53 | + "amplitude": int(np.ceil(np.log2(d)))} |
| 54 | + |
| 55 | + |
| 56 | +if __name__ == "__main__": |
| 57 | + import matplotlib.pyplot as plt |
| 58 | + from bookplot import savefig |
| 59 | + |
| 60 | + x = np.array([0.3, 1.2, 2.0, 0.7]) |
| 61 | + print("data vector x =", x) |
| 62 | + print("basis encoding of k = 5 on 3 qubits: ", basis_encoding(5, 3)) |
| 63 | + print("angle encoding (R_y), 4 qubits: ", angle_encoding(x)) |
| 64 | + amp = amplitude_encoding(x) |
| 65 | + print("amplitude encoding, 2 qubits: ", amp) |
| 66 | + assert np.allclose(amp.psi.real, x / np.linalg.norm(x)) |
| 67 | + # the angle-encoded state is a product state: its kernel factorizes |
| 68 | + xp = x + 0.4 |
| 69 | + overlap = abs(angle_encoding(x).overlap(angle_encoding(xp))) ** 2 |
| 70 | + assert np.isclose(overlap, np.prod(np.cos((x - xp) / 2) ** 2)) |
| 71 | + print(f"|<phi(x)|phi(x')>|^2 for the angle encoding = {overlap:.4f} = prod_i cos^2((x_i-x'_i)/2)") |
| 72 | + |
| 73 | + # Fig. 1-qubitcount |
| 74 | + ds = np.array([2, 4, 8, 16, 64, 256, 1024, 4096]) |
| 75 | + fig, ax = plt.subplots(figsize=(4.8, 3.3)) |
| 76 | + for name, style in (("basis (8 bits per feature)", "s-"), ("angle", "o-"), ("amplitude", "^-")): |
| 77 | + ax.loglog(ds, [qubits_needed(d)[name] for d in ds], style, label=name) |
| 78 | + ax.set_xlabel("number of features $d$"); ax.set_ylabel("qubits") |
| 79 | + ax.legend(); ax.grid(True, which="both", alpha=0.3) |
| 80 | + savefig(fig, 1, "qubitcount") |
| 81 | + |
| 82 | + # Fig. 1-periodic: one feature, one qubit, R_y(x)|0> = cos(x/2)|0> + sin(x/2)|1> |
| 83 | + xs = np.linspace(-2 * np.pi, 4 * np.pi, 400) |
| 84 | + p1 = np.sin(xs / 2) ** 2 |
| 85 | + fig, ax = plt.subplots(figsize=(4.8, 3.0)) |
| 86 | + ax.plot(xs, p1, label="$P(1) = \\sin^2(x/2)$") |
| 87 | + ax.axvspan(0, np.pi, color="tab:green", alpha=0.15, label="rescaled inputs $x\\in[0,\\pi]$") |
| 88 | + ax.set_xlabel("feature value $x$"); ax.set_ylabel("$P(\\mathrm{qubit}=1)$") |
| 89 | + ax.legend(loc="upper right", fontsize=8) |
| 90 | + savefig(fig, 1, "periodic") |
| 91 | + print("figures qubitcount and periodic written") |
0 commit comments