Mirror Circuit Fidelity Estimation#
!pip install qiskit-aer -q # qiskit-aer is REQUIRED by this notebook. This makes sure you have it.
import pygsti
from collections import defaultdict
import numpy as np
import time
# Create pyGSTi circs
unmapped_circs = [pygsti.circuits.Circuit([["Gxpi2", "Q0"], ["Gypi2", "Q1"]]),pygsti.circuits.Circuit([["Gypi2", "Q0"], ["Gxpi2", "Q1"]])]
Map circuits to device connectivity and U3-CX gate set#
This step will be different depending on what architecture you are using. For this example, we are using an IBM device. You need to end up with pyGSTi circuits in a U3-CX gate set so that circuit mirroring can be performed.
mapped_circs = defaultdict(list)
import qiskit
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager as _pass_manager
from qiskit_ibm_runtime.fake_provider import FakeSherbrooke, FakeAthensV2
fake_backend = FakeAthensV2()
pm = _pass_manager(coupling_map=fake_backend.coupling_map, basis_gates=['u3', 'cx'], optimization_level=0)
for i, circ in enumerate(unmapped_circs):
# Convert from pyGSTi to Qiskit
# Comment these lines out and do qiskit_circ = circ if passing in Qiskit
pygsti_openqasm_circ = circ.convert_to_openqasm(block_between_layers=True, include_delay_on_idle=False)
# print(pygsti_openqasm_circ)
qiskit_circ = qiskit.QuantumCircuit.from_qasm_str(pygsti_openqasm_circ)
# print(qiskit_circ.draw())
mapped_qiskit_circ = pm.run(qiskit_circ)
# print(mapped_qiskit_circ.draw())
pygsti_circ, _ = pygsti.circuits.Circuit.from_qiskit(mapped_qiskit_circ)
# print(pygsti_circ)
mapped_circ = pygsti_circ
metadata = {'width': len(mapped_circ.line_labels), 'depth': mapped_circ.depth, 'dropped_gates': 0, 'id': i}
mapped_circs[mapped_circ] += [metadata]
unmirrored_design = pygsti.protocols.FreeformDesign(mapped_circs)
Mirror circuit generation#
We use Pauli random compiling (pauli_rc) here. Central Pauli (central_pauli) is also an option.
# Highly recommended to seed all RNG
mcfe_rand_state = np.random.RandomState(20240718)
start = time.time()
mirror_design = pygsti.protocols.mirror_edesign.make_mirror_edesign(
unmirrored_design,
account_for_routing=False,
num_mcs_per_circ=100,
num_ref_per_qubit_subset=100,
mirroring_strategy='pauli_rc',
rand_state=mcfe_rand_state)
print(f'Mirroring time:', time.time() - start)
We have created the MCFE experiment design.
Run the Edesign#
This example will run the edesign on a fake IBM backend, but this is not strictly required. This step needs to generate a ProtocolData(edesign=mirror_edesign, dataset=circuit_counts_data) where mirror_edesign is the variable defined earlier and circuit_counts_data is a DataSet that contains the outcomes for each circuit.
from pygsti.extras.devices import ExperimentalDevice
from pygsti.extras import devices, ibmq
device = ExperimentalDevice.from_qiskit_backend(fake_backend)
pspec = device.create_processor_spec(['Gc{}'.format(i) for i in range(24)] + ['Gcnot'])
start = time.time()
exp = ibmq.IBMQExperiment(mirror_design, pspec, circuits_per_batch=300, num_shots=1024, seed=20240718, checkpoint_override=True)
print(time.time() - start)
from qiskit_aer import AerSimulator
sim_backend = AerSimulator.from_backend(fake_backend)
qiskit_convert_kwargs={}
start = time.time()
exp.transpile(sim_backend, direct_to_qiskit=True, qiskit_convert_kwargs=qiskit_convert_kwargs)
end = time.time()
print(f'Total transpilation time: {end - start}')
exp.submit(sim_backend)
start = time.time()
exp.batch_results = []
exp.retrieve_results()
end = time.time()
print(end - start)
data = exp.data
Compute process fidelity for each circuit#
from pygsti.protocols.vbdataframe import VBDataFrame
df = VBDataFrame.from_mirror_experiment(unmirrored_design, data)
If you used Central Pauli instead, you can swap 'RC Process Fidelity' for 'CP Process Fidelity' in the cell below.
process_fidelities = df.dataframe['RC Process Fidelity']
process_fidelities.tolist()