This tutorial contains a few details on how to run Mirror Randomized Benchmarking that are not covered in the RB overview tutorial.
Like Direct RB, Mirror RB is a streamlined RB method partly inspired by Clifford RB. 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. However, Mirror RB is even more streamlined than Direct RB, making it feasable on 10s or 100s of qubits (it is possible to holistically benchmark around $1/\epsilon$ qubits if the error rate per-gate per-qubit is around $\epsilon$).
Mirror RB can be implemented with non-Clifford gates, but pyGSTi currently only contains Mirror RB for Clifford gates. A depth $m$ ($m\geq 0$) mirror RB circuit consists of:
This construction means that Mirror RB circuits can be much shorter than Clifford RB circuits, or Direct RB circuits. Yet they still have the core randomization properties of both Clifford and Direct RB.
More information on Mirror RB will be added to this tutorial in a future release.
from __future__ import print_function #python 2 & 3 compatibility
import pygsti
Generating a Mirror RB experiment design is very similar to creating a Direct RB design. The only difference is that there is no compilation in a Mirror RB circuit, so there is no compilation algorithm to tweak.
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
).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 (but where we don't include the randomized Pauli gates in the layer count).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.
nQubits = 4
qubit_labels = ['Q'+str(i) for i in range(nQubits)]
gate_names = ['Gi', 'Gxpi2', 'Gxpi', 'Gxmpi2', 'Gypi2', 'Gypi', 'Gympi2',
'Gzpi2', 'Gzpi', 'Gzmpi2', 'Gcphase']
availability = {'Gcphase':[('Q'+str(i),'Q'+str((i+1) % nQubits)) for i in range(nQubits)]}
pspec = pygsti.obj.ProcessorSpec(nQubits, gate_names, availability=availability,
construct_clifford_compilations={'absolute': ('paulis', '1Qcliffords')},
qubit_labels=qubit_labels, construct_models=('clifford',))
depths = [0, 2, 4, 8, 16, 32]
k = 40
qubits = qubit_labels
All other arguments to the Mirect RB experiment design generation function MirectRBDesign
are optional. But, as with Direct RB, to make the most out of Mirect RB it is typically important to at least understand the circuit layer sampling.
Exactly as with Direct RB, the Mirect RB circuit layer sampling distribution $\Omega$ is perhaps the most important input to the Mirect RB experiment design. This is because, by construction, the Mirect RB error rate $r$ is $\Omega$-dependent. 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!
This might seem like a drawback in comparison to Clifford RB, but note that this $\Omega$-dependence is analogous to the Clifford-compiler dependence of the Clifford RB error rate (with the advantage that it is more easily controlled and understood). And Mirror RB can be run on many, many more qubits!
The sampling distribution is specified via the optional arguements sampler
and samplerargs
. Here we use what we call the "edge grab" sampler.
Because both Direct and Mirror RB have the this sampling-distribution dependence, there is a separate random circuit sampling tutorial that introduces the different built-in sampling algorithms within pyGSTi (which includes details of the "edge grab" algorithm).
sampler = 'edgegrab'
samplerargs = [0.5]
From here, generating the design and collecting data proceeds as in the RB overview tutorial.
# Here we construct an error model with 5% local depolarization on each qubit after each gate.
gate_error_rate = 0.02
def simulate_taking_data(data_template_filename):
"""Simulate taking data and filling the results into a template dataset.txt file"""
pspec = pygsti.obj.ProcessorSpec(nQubits, gate_names, availability=availability,
qubit_labels=qubit_labels, construct_models=('TP',))
noisemodel = pspec.models['TP'].copy()
for gate in noisemodel.operation_blks['gates'].values():
if gate.dim == 16:
gate.depolarize(1 - pygsti.tools.rbtools.r_to_p(1 - (1-gate_error_rate)**2, 4))
if gate.dim == 4:
gate.depolarize(1 - pygsti.tools.rbtools.r_to_p(gate_error_rate, 2))
pygsti.io.fill_in_empty_dataset_with_fake_data(noisemodel, data_template_filename, nSamples=1000, seed=1234)
design = pygsti.protocols.MirrorRBDesign(pspec, depths, k, qubit_labels=qubits, sampler=sampler,
samplerargs=samplerargs)
pygsti.io.write_empty_protocol_data(design, '../tutorial_files/test_rb_dir', clobber_ok=True)
# -- fill in the dataset file in tutorial_files/test_rb_dir/data/dataset.txt --
simulate_taking_data('../tutorial_files/test_rb_dir/data/dataset.txt') # REPLACE with actual data-taking
data = pygsti.io.load_data_from_dir('../tutorial_files/test_rb_dir')
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)
# The error rate we *approximately* expect accord to Mirror RB theory
print(1 - (1 - gate_error_rate)**(2 * len(qubits)))