This tutorial contains a few details on how to run Clifford Randomized Benchmarking that are not covered in the RB overview tutorial.
By Clifford randomized benchmarking we mean RB of the $n$-qubit Clifford group, as defined by Magesan et al. in Scalable and Robust Benchmarking of Quantum Processes. This protocol is routinely run on 1 and 2 qubits.
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
The only aspects of running Clifford RB with pyGSTi that are not covered in the RB overview tutorial are some subtleties in generating a Clifford RB experiment design (and what those subtleties mean for interpretting the results). To cover these subtleties, here we go through the inputs used to generate a Clifford RB experiment design in more detail.
The first inputs to create an 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 Clifford RB on $n$ qubits, the RB depth is the number of (uncompiled) $n$-qubit Clifford gates in the sequence minus two. This convention is chosen so that zero is the minimum RB depth for all RB methods in pyGSTi.k
).qubits
).All other arguments to Clifford RB experiment design generation function are optional.
n_qubits = 4
qubit_labels = ['Q0','Q1','Q2','Q3']
gate_names = ['Gxpi2', 'Gxmpi2', 'Gypi2', 'Gympi2', 'Gcphase']
availability = {'Gcphase':[('Q0','Q1'), ('Q1','Q2'), ('Q2','Q3'), ('Q3','Q0')]}
pspec = QPS(n_qubits, gate_names, availability=availability, qubit_labels=qubit_labels)
compilations = {'absolute': CCR.create_standard(pspec, 'absolute', ('paulis', '1Qcliffords'), verbosity=0),
'paulieq': CCR.create_standard(pspec, 'paulieq', ('1Qcliffords', 'allcnots'), verbosity=0)}
depths = [0,1,2,4,8]
k = 10
qubits = ['Q0','Q1']
In the standard formulation of Clifford RB, the circuit should always return the all-zeros bit-string if there is no errors. But it can be useful to randomized the "target" bit-string (e.g., then the asymptote in the RB decay is fixed to $1/2^n$ even with biased measurement errors). This randomization is specified via the randomizeout
argument, and it defaults to False
(the standard protocol).
randomizeout = True
To generate a Clifford RB circuit in terms of native gates, it is necessary to decompose each $n$-qubit Clifford gate into the native gates. pyGSTi has a few different Clifford gate compilation algorithms, that can be accessed via the compilerargs
optional argument. Note: The Clifford RB error rate is compiler dependent! So it is not possible to properly interpret the Clifford RB error rate without understanding at least some aspects of the compilation algorithm (e.g., the mean two-qubit gate count in a compiled $n$-qubit Clifford circuit). This is one of the reasons that Direct RB is arguably a preferable method to Clifford RB.
None of the Clifford compilation algorithms in pyGSTi are a simple look-up table with some optimized property (e.g., minimized two-qubit gate count or depth). Look-up tables like this are typically used for 1- and 2-qubit Clifford RB experiments, but we instead used a method that scales to many qubits.
There are multiple compilation algorithms in pyGSTi, and the algorithm can be set using the compilerargs
argument (see the pygsti.algorithms.compile_clifford
function for some details on the available algorithms, and the CliffordRBDesign
docstring for how to specify the desired algorithm). The default algorthm is the one that we estimate to be our "best" algorithm in the regime of 1-20ish qubits. This algorithm (and some of the other algorithms) are randomized. So when creating a CliffordRBDesign
you can also specify the number of randomization, via citerations
. Increasing this will reduce the average depth and two-qubit gate count of each $n$-qubit Clifford gate, up to a point, making Clifford RB feasable on more qubits.
But note that time to generate the circuits can increase quickly as citerations
increases (because a depth $m$ circuit contains $(m+2)$ $n$-qubit Clifford gates to compile).
citerations = 20
From here, everything proceeds as in the RB overview tutorial (except for adding in the optional arguments).
# Here we construct an error model with 1% local depolarization on each qubit after each gate.
def simulate_taking_data(data_template_filename):
"""Simulate taking data and filling the results into a template dataset.txt file"""
noisemodel = pygsti.models.create_crosstalk_free_model(pspec, depolarization_strengths={g:0.01 for g in pspec.gate_names})
pygsti.io.fill_in_empty_dataset_with_fake_data(data_template_filename, noisemodel, num_samples=1000, seed=1234)
design = pygsti.protocols.CliffordRBDesign(pspec, compilations, depths, k, qubit_labels=qubits,
randomizeout=randomizeout, citerations=citerations)
pygsti.io.write_empty_protocol_data('../tutorial_files/test_crb_dir', design, clobber_ok=True)
# -- fill in the dataset file in tutorial_files/test_rb_dir/data/dataset.txt --
simulate_taking_data('../tutorial_files/test_crb_dir/data/dataset.txt') # REPLACE with actual data-taking
data = pygsti.io.read_data_from_dir('../tutorial_files/test_crb_dir')
protocol = pygsti.protocols.RB()
results = protocol.run(data)
ws = pygsti.report.Workspace()
ws.init_notebook_mode(autodisplay=True)
ws.RandomizedBenchmarkingPlot(results)
- Sampling 10 circuits at CRB length 0 (1 of 5 depths) with seed 75116 - Sampling 10 circuits at CRB length 1 (2 of 5 depths) with seed 75126 - Sampling 10 circuits at CRB length 2 (3 of 5 depths) with seed 75136 - Sampling 10 circuits at CRB length 4 (4 of 5 depths) with seed 75146 - Sampling 10 circuits at CRB length 8 (5 of 5 depths) with seed 75156
--------------------------------------------------------------------------- FileNotFoundError Traceback (most recent call last) File ~\Documents\pyGSTi_random_bugfixes\pygsti\io\readers.py:98, in read_dataset(filename, cache, collision_action, record_zero_counts, ignore_zero_count_lines, with_times, circuit_parse_cache, verbosity) 96 try: 97 # a saved Dataset object is ok ---> 98 ds = _data.DataSet(file_to_load_from=filename) 99 except: 100 101 #Parser functions don't take a VerbosityPrinter yet, and so 102 # always output to stdout (TODO) File ~\Documents\pyGSTi_random_bugfixes\pygsti\data\dataset.py:1003, in DataSet.__init__(self, oli_data, time_data, rep_data, circuits, circuit_indices, outcome_labels, outcome_label_indices, static, file_to_load_from, collision_action, comment, aux_info) 1000 assert(oli_data is None and time_data is None and rep_data is None 1001 and circuits is None and circuit_indices is None 1002 and outcome_labels is None and outcome_label_indices is None) -> 1003 self.read_binary(file_to_load_from) 1004 return File ~\Documents\pyGSTi_random_bugfixes\pygsti\data\dataset.py:2950, in DataSet.read_binary(self, file_or_filename) 2949 else: -> 2950 f = open(file_or_filename, "rb") 2951 else: FileNotFoundError: [Errno 2] No such file or directory: '../tutorial_files/test_rb_dir/data/dataset_crb.txt' During handling of the above exception, another exception occurred: FileNotFoundError Traceback (most recent call last) Cell In[6], line 7 4 pygsti.io.write_empty_protocol_data('../tutorial_files/test_rb_dir', design, clobber_ok=True) 6 # -- fill in the dataset file in tutorial_files/test_rb_dir/data/dataset.txt -- ----> 7 simulate_taking_data('../tutorial_files/test_rb_dir/data/dataset_crb.txt') # REPLACE with actual data-taking 9 data = pygsti.io.read_data_from_dir('../tutorial_files/test_rb_dir') 11 protocol = pygsti.protocols.RB() Cell In[5], line 5, in simulate_taking_data(data_template_filename) 3 """Simulate taking data and filling the results into a template dataset.txt file""" 4 noisemodel = pygsti.models.create_crosstalk_free_model(pspec, depolarization_strengths={g:0.01 for g in pspec.gate_names}) ----> 5 pygsti.io.fill_in_empty_dataset_with_fake_data(data_template_filename, noisemodel, num_samples=1000, seed=1234) File ~\Documents\pyGSTi_random_bugfixes\pygsti\io\writers.py:631, in fill_in_empty_dataset_with_fake_data(dataset_filename, model, num_samples, sample_error, seed, rand_state, alias_dict, collision_action, record_zero_counts, comm, mem_limit, times, fixed_column_mode) 628 model, dataset_filename = dataset_filename, model 630 from pygsti.data.datasetconstruction import simulate_data as _simulate_data --> 631 ds_template = _readers.read_dataset(dataset_filename, ignore_zero_count_lines=False, with_times=False, verbosity=0) 632 ds = _simulate_data(model, list(ds_template.keys()), num_samples, 633 sample_error, seed, rand_state, alias_dict, 634 collision_action, record_zero_counts, comm, 635 mem_limit, times) 636 if fixed_column_mode == "auto": File ~\Documents\pyGSTi_random_bugfixes\pygsti\io\readers.py:133, in read_dataset(filename, cache, collision_action, record_zero_counts, ignore_zero_count_lines, with_times, circuit_parse_cache, verbosity) 130 else: 131 # otherwise must use standard dataset file format 132 parser = _stdinput.StdInputParser() --> 133 ds = parser.parse_datafile(filename, bToStdout, 134 collision_action=collision_action, 135 record_zero_counts=record_zero_counts, 136 ignore_zero_count_lines=ignore_zero_count_lines, 137 with_times=with_times) 138 return ds File ~\Documents\pyGSTi_random_bugfixes\pygsti\io\stdinput.py:403, in StdInputParser.parse_datafile(self, filename, show_progress, collision_action, record_zero_counts, ignore_zero_count_lines, with_times) 401 preamble_directives = {} 402 preamble_comments = [] --> 403 with open(filename, 'r') as datafile: 404 for line in datafile: 405 line = line.strip() FileNotFoundError: [Errno 2] No such file or directory: '../tutorial_files/test_rb_dir/data/dataset_crb.txt'