Skip to content

Quantum Phase Estimation for a Matrix

This notebook demonstrates the capability of Classiq's Synthesis engine to reduce depth and cx-counts when modeling a Quantum Phase Estimation (QPE) on a unitary that is hard-coded unitary matrix (of the form \(e^{2\pi i A}\), with \(A\) Hermitian).

import numpy as np
import scipy

# taking a random example, rescaling and shifting the matrix to guarantee eigenvalues in [0,1)
np.random.seed(1235)
new_mat = np.random.rand(8, 8)
new_mat = (new_mat + new_mat.T) / 2

w, v = np.linalg.eig(new_mat)
w_max = np.max(np.abs(w))

mew_mat = (new_mat + w_max) / (2 * w_max)

my_unitary = scipy.linalg.expm(1j * 2 * np.pi * new_mat)

precisions = [l for l in range(1, 9)]
print("precisions:", precisions)
precisions: [1, 2, 3, 4, 5, 6, 7, 8]
transpilation_options = {"classiq": "custom", "qiskit": 3}

1. Classiq's QPE

from classiq import (
    CustomHardwareSettings,
    Preferences,
    QArray,
    QNum,
    QuantumProgram,
    allocate,
    allocate_num,
    create_model,
    qfunc,
    qpe,
    set_preferences,
    show,
    synthesize,
    unitary,
)

classiq_depths = []
classiq_cx_counts = []

preferences = Preferences(
    custom_hardware_settings=CustomHardwareSettings(basis_gates=["cx", "u"]),
    transpilation_option=transpilation_options["classiq"],
)

for precision in precisions:

    @qfunc
    def main():
        phase = QNum("phase")
        state = QArray("state")
        allocate(3, state)
        allocate_num(precision, False, precision, phase)
        qpe(
            unitary=lambda: unitary(elements=my_unitary.tolist(), target=state),
            phase=phase,
        )

    qmod = create_model(main)
    qmod = set_preferences(qmod, preferences=preferences)
    qprog = synthesize(qmod)
    circuit = QuantumProgram.from_qprog(qprog)
    depth_classiq = circuit.transpiled_circuit.depth
    classiq_depths.append(depth_classiq)
    classiq_cx_counts.append(circuit.transpiled_circuit.count_ops["cx"])

print("classiq depths:", classiq_depths)
print("classiq cx-counts:", classiq_cx_counts)
classiq depths: [180, 363, 546, 731, 916, 1101, 1286, 1471]
classiq cx-counts: [96, 195, 294, 396, 500, 606, 714, 824]

2. Comparing to Qiskit Implementations

The qiskit data was generated using qiskit version 1.0.0. To run the qiskit code uncomment the commented cells below.

qiskit_depths = [320, 950, 2206, 4706, 9694, 19658, 39574, 79394]
qiskit_cx_counts = [162, 484, 1124, 2398, 4938, 10008, 20136, 40378]
# from importlib.metadata import version
# try:
#     import qiskit
#     if version('qiskit') != "1.0.0":
#       !pip uninstall qiskit -y
#       !pip install qiskit==1.0.0
# except ImportError:
#     !pip install qiskit==1.0.0
# from qiskit import QuantumCircuit, QuantumRegister, transpile
# from qiskit.circuit.library import PhaseEstimation

# q = QuantumRegister(3, "q")
# qc = QuantumCircuit(q)
# qc.unitary(my_unitary.tolist(), q)

# qiskit_depths = []
# qiskit_cx_counts = []
# for precision in precisions:
#     qpe_qc = PhaseEstimation(precision, qc)
#     transpiled_cir = transpile(
#         qpe_qc,
#         basis_gates=["u", "cx"],
#         optimization_level=transpilation_options["qiskit"],
#     )
#     qiskit_depths.append(transpiled_cir.depth())
#     qiskit_cx_counts.append(transpiled_cir.count_ops()["cx"])

# print("qiskit depths:", qiskit_depths)
# print("qiskit cx-counts:", qiskit_cx_counts)

3. Plotting the Data

import matplotlib.pyplot as plt

classiq_color = "#119DA4"
qiskit_color = "#bb8bff"
plt.rcParams["font.family"] = "serif"
plt.rc("savefig", dpi=300)
plt.rcParams["axes.linewidth"] = 1
plt.rcParams["xtick.major.size"] = 5
plt.rcParams["xtick.minor.size"] = 5
plt.rcParams["ytick.major.size"] = 5
plt.rcParams["ytick.minor.size"] = 5

plt.rcParams["axes.linewidth"] = 1
plt.rcParams["xtick.major.size"] = 5
plt.rcParams["xtick.minor.size"] = 5
plt.rcParams["ytick.major.size"] = 5
plt.rcParams["ytick.minor.size"] = 5


(classiq1,) = plt.semilogy(
    precisions,
    classiq_depths,
    "-o",
    label="classiq depth",
    markerfacecolor=classiq_color,
    markeredgecolor="k",
    markersize=8,
    markeredgewidth=1.5,
    linewidth=1.5,
    color=classiq_color,
)
(classiq2,) = plt.semilogy(
    precisions,
    classiq_cx_counts,
    "-*",
    label="classiq cx-counts",
    markerfacecolor=classiq_color,
    markeredgecolor="k",
    markersize=12,
    markeredgewidth=1.5,
    linewidth=1.5,
    color=classiq_color,
)
(qiskit1,) = plt.semilogy(
    precisions,
    qiskit_depths,
    "-s",
    label="qiskit depth",
    markerfacecolor=qiskit_color,
    markeredgecolor="k",
    markersize=7,
    markeredgewidth=1.5,
    linewidth=1.5,
    color=qiskit_color,
)
(qiskit2,) = plt.semilogy(
    precisions,
    qiskit_cx_counts,
    "-v",
    label="qiskit cx-counts",
    markerfacecolor=qiskit_color,
    markeredgecolor="k",
    markersize=8,
    markeredgewidth=1.5,
    linewidth=1.5,
    color=qiskit_color,
)

first_legend = plt.legend(
    handles=[qiskit1, qiskit2],
    fontsize=16,
    loc="lower left",
    bbox_to_anchor=(0.1, 0.75),
)
ax = plt.gca().add_artist(first_legend)
plt.legend(handles=[classiq1, classiq2], fontsize=16, loc="lower right")

plt.ylabel("Depth, CX-counts", fontsize=16)
plt.xlabel("QPE-precision", fontsize=16)
plt.yticks(fontsize=16)
plt.xticks(fontsize=16)
plt.text(0.9, 0.65e5, "(a)", fontsize=16)
Text(0.9, 65000.0, '(a)')

png