This tutorial contains a few details on how to run Mirror Randomized Benchmarking with universal gate sets, that are not covered in the RB overview tutorial or the Clifford MRB tutorial.
Mirror RB is a streamlined, computationally-efficient RB method. It has the same core purpose as Clifford RB - quantifying average gate performance - but it is feasable on more qubits, and it provides more directly useful information. Unlike oter RB protocols, Mirror RB can be implemented with non-Clifford gates on many qubits. The general structure of MRB circuits with non-Clifford gates is similar to that of MRB circuits with Clifford gates. The structure of a depth $m$ ($m\geq 0$) mirror RB circuit is:
This circuit then undergoes a version of randomized compilation to get the final circuit, in which random Pauli gates are compiled into the composite layers.
See Demonstrating scalable randomized benchmarking of universal gate sets for further details on MRB with universal gate sets.
from __future__ import print_function #python 2 & 3 compatibility
import pygsti
from pygsti.processors import QubitProcessorSpec as QPS
from pygsti.processors import CliffordCompilationRules as CCR
Generating a Mirror RB experiment design for universal gate sets is very similar to creating an experiment design for other RB methods or for Clifford Mirror RB.
The first inputs to create a Mirror RB experiment design are the same as in all RB protocols, and these are covered in the RB overview tutorial. They are:
pspec
). Universal gate set MRB in pyGSTi currently requires the Gzr
and Xpi2
gates to be in the list of gate names.depths
). For Mirror RB, these depths must be even integers. They correspond to the number of total layers in the "compute" and "uncompute" sub-circuits.k
).qubits
).# Mirror RB can be run on many many more qubit than this, but this notebook creates simulated data. As
# we are using a full density matrix simulator this limits the number of qubits we can use here.
n_qubits = 3
qubit_labels = ['Q'+str(i) for i in range(n_qubits)]
gate_names = ['Gi', 'Gxpi2', 'Gzr', 'Gcphase']
availability = {'Gcphase':[('Q'+str(i),'Q'+str((i+1) % n_qubits)) for i in range(n_qubits)]}
pspec = QPS(n_qubits, gate_names, availability=availability, qubit_labels=qubit_labels)
depths = [0, 2, 4, 8, 16, 32]
k = 40
qubits = qubit_labels
Mirror RB is implemented in pyGSTi for specific random circuit structures, specified via the option argument circuit_type
:
clifford+zxzxz-haar
: Clifford two-qubit gates and Haar-random 1-qubit gatesclifford+zxzxz-clifford
: Clifford two-qubit gates and random Clifford 1-qubit gates (this is not a universal gate set)cz(theta)+zxzxz-haar
: Controlled Z axis rotation gates CZ(theta) and Haar-random 1-qubit gates. This option requires that both CZ(theta) and CZ(-theta) are in the allowed two-qubit gates.All single-qubit gates are decomposed into a series of five single qubit gates: Two $X_{\pi/2}$ gates and three $Z(\theta)$ gates.
As with Clifford Mirror RB, the Mirror RB error rate $r$ depends on the circuit layer sampling distribution $\Omega$. This $\Omega$-dependence is useful, because by carefully choosing or varying $\Omega$ we can learn a lot about device performance. But it also means that the $\Omega$ has to be carefully chosen! At the very least, you need to know what sampling distribution you are using in order to interpret the results!
Universal gate set MRB in pyGSTi uses the "edge grab" sampler. The frequency of two-qubit gates in the layers can be changed via the optional arguement samplerargs
.
samplerargs = [0.5]
From here, generating the design and collecting data proceeds as in the RB overview tutorial.
qubit_error_rate = 0.002
def simulate_taking_data(data_template_filename):
"""Simulate taking data and filling the results into a template dataset.txt file"""
error_rates = {}
for gn in pspec.gate_names:
n = pspec.gate_num_qubits(gn)
gate_error_rate = n * qubit_error_rate
error_rates[gn] = [gate_error_rate/(4**n - 1)] * (4**n - 1)
noisemodel = pygsti.models.create_crosstalk_free_model(pspec, stochastic_error_probs=error_rates)
pygsti.io.fill_in_empty_dataset_with_fake_data(data_template_filename, noisemodel, num_samples=1000, seed=1234)
design = pygsti.protocols.MirrorRBDesign(pspec, depths, k, qubit_labels=qubits,
circuit_type='clifford+zxzxz-haar', samplerargs=samplerargs)
pygsti.io.write_empty_protocol_data('../tutorial_files/test_mrb_dir', design, clobber_ok=True)
# -- fill in the dataset file in tutorial_files/test_rb_dir/data/dataset.txt --MirrorRBDesign
simulate_taking_data('../tutorial_files/test_mrb_dir/data/dataset.txt') # REPLACE with actual data-taking
data = pygsti.io.read_data_from_dir('../tutorial_files/test_mrb_dir')
- Sampling 40 circuits at MRB length 0 (1 of 6 depths) with seed 459117 - Sampling 40 circuits at MRB length 2 (2 of 6 depths) with seed 459157 - Sampling 40 circuits at MRB length 4 (3 of 6 depths) with seed 459197 - Sampling 40 circuits at MRB length 8 (4 of 6 depths) with seed 459237 - Sampling 40 circuits at MRB length 16 (5 of 6 depths) with seed 459277 - Sampling 40 circuits at MRB length 32 (6 of 6 depths) with seed 459317
As with all RB methods in pyGSTi, to analyze the data we instantiate an RB
protocol and .run
it on our data object. However, there is a slight difference for Mirror RB. Mirror RB doesn't fit simple success/fail format data: instead it fits what we call Hamming weight adjusted success probabilities to an exponential decay ($P_m = A + B p^m$ where $P_m$ is the average adjusted success probability at RB length $m$).
To obtain this data analysis we simply specify the data type when instantiate an RB
protocol: we set datatype = adjusted_success_probabilities
.
protocol = pygsti.protocols.RB(datatype = 'adjusted_success_probabilities', defaultfit='A-fixed')
results = protocol.run(data)
ws = pygsti.report.Workspace()
ws.init_notebook_mode(autodisplay=True)
ws.RandomizedBenchmarkingPlot(results)
<pygsti.report.workspaceplots.RandomizedBenchmarkingPlot at 0x7fcf2deafa60>