QCut Basic Usage

QCut Basic Usage#

# Needs QCut[iqm] install otherwise use
# from qiskit_aer import AerSimulator
# as backend.
from iqm.qiskit_iqm import IQMFakeAdonis
from qiskit import QuantumCircuit, transpile
from qiskit.circuit.library import CXGate
from qiskit.primitives import (
    BackendEstimatorV2 as BackendEstimator,
)
from qiskit.primitives import (
    StatevectorEstimator,
)
from qiskit.quantum_info import SparsePauliOp
from qiskit_aer import AerSimulator

import QCut as ck
from QCut import cut, cutGate
/home/nivalajo/work/QCut/.venv/lib/python3.12/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html
  from .autonotebook import tqdm as notebook_tqdm
# define initial circuit

circuit = QuantumCircuit(5)

for qubit in range(5):
    circuit.ry(0.4 + 0.3 * qubit, qubit)
circuit.cx(0, 1)
circuit.cx(1, 2)
circuit.cx(2, 3)
circuit.cx(3, 4)

circuit.draw("mpl")
../_images/94fb09acc9fa14a4349e56957dfbc91771f9e5b1ccd3e1b8a51765a5840998ab.png
# insert cuts
marked_circuit = QuantumCircuit(5)

for qubit in range(5):
    marked_circuit.ry(0.4 + 0.3 * qubit, qubit)
marked_circuit.cx(0, 1)
marked_circuit.append(**cutGate(CXGate(), 1, 2))
marked_circuit.cx(2, 3)
marked_circuit.append(cut(), [3])
marked_circuit.cx(3, 4)

marked_circuit.decompose(["CutGate"]).draw("mpl")
../_images/00f955498039a274219d52e19bab08381a216aa3cba8eb4e0f3ab7fa7539e089.png
# extract cut locations and separate into subcircuits

cut_circuit = ck.get_locations_and_subcircuits(marked_circuit)
cut_circuit.subcircuits[0].draw("mpl")
../_images/2216ba06dc6eecc6c5af4ecd5756778451097dbc48b1ab1ed5403dff2aa1dfef.png
cut_circuit.subcircuits[1].draw("mpl")
../_images/ee6d4a90fe67115fdde50c75fec94d1d4bcf6c4678a2c597940775a4ad4d82d6.png
cut_circuit.subcircuits[2].draw("mpl")
../_images/32130e8222a73ba09b996f6092b0a740df4e8affa1c93c970688896257d7a4b6.png
# define backends
fake = IQMFakeAdonis()
sim = AerSimulator()
# transpile subcircuits for backend

transpiled = ck.transpile_subcircuits(cut_circuit, fake, optimization_level=3)
# generate experiment circuits
observables = SparsePauliOp(["IIIIZ", "IIIZI", "IIZII", "IIIZZ"])

cut_experiment = ck.get_experiment_circuits(transpiled, observables)
# run experiment circuits
# run_experiments() also post processes the results

results = ck.run_experiments(cut_experiment, backend=fake)
# get the approximated expectation values
expectation_values = ck.estimate_expectation_values(results)
obs = [ob.to_label() for ob in observables.paulis]

estimator = StatevectorEstimator()
exact_expvals = [
    e.data.evs
    for e in estimator.run([(x) for x in zip([circuit] * len(obs), obs)]).result()
]


tr = transpile(circuit, backend=fake)

tr_obs = observables.apply_layout(tr.layout)

tr_obs_separate = [SparsePauliOp(pauli.to_label()) for pauli in tr_obs.paulis]

fake_estimator = BackendEstimator(backend=fake)
exps = [
    e.data.evs
    for e in fake_estimator.run(
        [(x) for x in zip([tr] * len(tr_obs_separate), tr_obs_separate)]
    ).result()
]
import numpy as np

np.set_printoptions(formatter={"float": lambda x: f"{x:0.6f}"})

print(f"QCut expectation values:{np.array(expectation_values)}")
print(f"Noisy expectation values with fake backend:{np.array(exps)}")
print(f"Exact expectation values with ideal simulator :{np.array(exact_expvals)}")
QCut expectation values:[0.808219 0.649909 0.285554 0.621305]
Noisy expectation values with fake backend:[0.865723 0.568848 0.307617 0.609375]
Exact expectation values with ideal simulator :[0.921061 0.704466 0.380625 0.764842]