Improve Quantum Volume via compilation

This notebook demonstrates some compiler features that were introduced in [1] (see below). This includes dynamical decoupling, mapping via binary integer programming, and pulse-efficient SU(4) decomposition. These techniques are applicable to improving the fidelity of any circuit. Here we demonstrate their utility in Quantum Volume calculations on an IBM superconducting device.

A custom passmanager has been built for this purpose, to invoke these passes. You can view the code in

Essential to this demonstration is faster CNOT gates and improved readout, as also detailed in the paper.

[1] Jurcevic, Petar, Ali Javadi-Abhari, Lev S. Bishop, Isaac Lauer, Daniela F. Bogorin, Markus Brink, Lauren Capelluto et al. “Demonstration of quantum volume 64 on a superconducting quantum computing system.” Quantum Science and Technology 6, no. 2 (2021): 025020.

I. Identify the device and region on which to measure volume

# choose a provider and backend you have access to

from qiskit import IBMQ
provider = IBMQ.providers()[5]
backend = provider.backend.ibm_hanoi
from qiskit.transpiler import InstructionDurations, CouplingMap

qubit_subset = [1, 2, 3, 5, 8, 11]

coupling_map = CouplingMap(backend.configuration().coupling_map)
basis_gates = backend.configuration().basis_gates
dt = backend.configuration().dt

instruction_durations = InstructionDurations.from_backend(backend)

II. Load pre-mapped circuits

If you do not have access to a full CPLEX license or if you want to speed up the compilation process, you can load pre-mapped circuits and set do_mapping=False below. If you are using the same qubit connecitivity topology, this can be a good way to skip the CPLEX optimization part of compilation, since the mapping problem is dependent only on the topology. Setting do_mapping=True will do the entire compilation flow in Qiskit.

do_mapping = False
from qiskit.circuit import qpy_serialization
with open('qv64_original.qpy', 'rb') as f:
    model_circuits = qpy_serialization.load(f)
with open('qv64_bip_line_approx99.qpy', 'rb') as f:
    model_circuits_mapped = qpy_serialization.load(f)

III. Build a custom passmanager that will do the desired circuit mapping and optimizations

from qv_tools import qv_passmanager
pm = qv_passmanager(basis_gates=basis_gates,
# optional: visualize the passmanager to see the passes invoked

IV. Demonstrate compilation on a single model circuit

from qiskit.circuit.library import QuantumVolume
num_qubits = 6
model_circuit = QuantumVolume(num_qubits=num_qubits, seed=15).decompose()
model_circuit.draw('mpl', fold=-1)
model_circuit_measured = model_circuit.measure_all(inplace=False)
model_circuit_compiled =
print(f"num cx: {model_circuit_compiled.count_ops().get('cx', 0)}")
print(f"num sx: {model_circuit_compiled.count_ops().get('sx', 0)}")
print(f"num x: {model_circuit_compiled.count_ops().get('x', 0)}")
print(f"depth: {model_circuit_compiled.depth()}")
print(f"duration: {model_circuit_compiled.duration * dt * 1e6} us")
# optional: visualize the compiled circuit
model_circuit_compiled.draw('mpl', fold=-1, idle_wires=False)
# optional: visualize the first 10,000 dt's of events on a timeline
from qiskit.visualization import timeline_drawer
timeline_drawer(model_circuit_compiled, time_range=(0, 10000), show_idle=False)

V. Compile 500 model circuits and plot distribution of resources

from tqdm import tqdm

model_circuits = [QuantumVolume(num_qubits, seed=i).decompose() for i in range(500)]
model_circuits_measured = [circuit.measure_all(inplace=False) for circuit in model_circuits]

model_circuits_compiled = []
for circuit in tqdm(model_circuits_measured):
from qv_tools import plot_resources
plot_resources(model_circuits_compiled, dt)

VI. Run experiments and compute HOPs

from qiskit.providers.aer import AerSimulator
from qiskit.providers.aer.noise import NoiseModel
fake_backend = AerSimulator()
shots = 2000
job_exp =, shots=shots, rep_delay=0.0005)
job_sim =, shots=shots)
# optional: inquire about job status
print('status: ', job_exp.status())
print('queue position: ', job_exp.queue_position())
print('job id: ', job_exp.job_id())
from qv_tools import get_ideal_probabilities, get_heavy_strings, hop

hops_exp = []
hops_sim = []

for model_circuit in model_circuits:
    ideal_probs = get_ideal_probabilities(model_circuit)
    median, heavy_strings = get_heavy_strings(ideal_probs)

    counts_exp = job_exp.result().get_counts(model_circuit)
    counts_sim = job_sim.result().get_counts(model_circuit)

    hops_exp.append(hop(counts_exp, ideal_probs))
    hops_sim.append(hop(counts_sim, ideal_probs))
from qv_tools import plot_hop_accumulative

This notebook has been written in a self-contained way, in order to demonstrate the capabilities of the compiler and the faster gates. The qiskit-experiments framework includes more bells and whistles for running quantum volume experiments, as well as other experiments.