This tutorial will demonstrate how to run an experiment on IBM Q Processors. To do so you will need QisKit installed and an IBM Q account.
This was last run with QisKit versions:
import pygsti
from pygsti.extras import devices
from pygsti.extras import ibmq
import qiskit
First, load you IBM Q account, get your provider
and select a device. To do this, follow IBM Q's instructions.
provider = qiskit.IBMQ.load_account()
provider = qiskit.IBMQ.get_provider()
for p in provider.backends():
print(p)
dev_name = 'ibmq_belem'
backend = provider.get_backend(dev_name)
Next we create a ProcessorSpec for the device you're going to run on. If you're using a device that isn't currently included in the extras.devices module you'll need to create this yourself, e.g., by creating a new .py file in extras/devices with the same details as the others.
This ProcessorSpec must also contain the details needed for creating the pyGSTi experiment design that you want to run, which you can tweak by varying the optional arguments to the devices.create_processor_spec()
function.
pspec = devices.create_processor_spec(dev_name, ['Gc{}'.format(i) for i in range(24)],
construct_models=('clifford',))
Next we create an ExperimentDesign
that specifies the circuits you want to run on that device. Here we create a very simple mirror circuit benchmarking experiment. We'll use randomized mirror circuits, constructed using a MirrorRBDesign
.
First we pick the circuit design parameters:
#circuit design parameters
depths = [0, 2, 4, 16, 32, 64]
circuits_per_shape = 20
# dict setting the circuit widths (# qubits) you want to probe
# and the qubits you want to use at each width
qubit_lists = {}
qubit_lists[1] = [('Q0',),]
qubit_lists[2] = [('Q0', 'Q1'),]
qubit_lists[3] = [('Q0', 'Q1', 'Q2'),]
qubit_lists[4] = [('Q0', 'Q1', 'Q2', 'Q3')]
qubit_lists[5] = [('Q0', 'Q1', 'Q2', 'Q3', 'Q4')]
widths = list(qubit_lists.keys())
print('total circuits: {}'.format(circuits_per_shape*len(widths)*len(depths)))
total_circuits = 0
for w in widths:
total_circuits += len(qubit_lists[w]) * circuits_per_shape * len(depths)
print('full total circuits: {}'.format(total_circuits) )
# We'll use the `edgegrab` sampler, which requires specifying the expected number
# of two-qubit gates per random layer.
twoQmean = {w:w/8 for w in widths}
if 1 in widths: twoQmean[1] = 0 # No two-qubit gates in one-qubit circuits.
edesigns_dict = {}
edesign_index = 1
for w in widths:
for qubits in qubit_lists[w]:
sub_edesign = pygsti.protocols.MirrorRBDesign(pspec, depths, circuits_per_shape, qubit_labels=qubits,
sampler='edgegrab', samplerargs=[twoQmean[w],])
edesigns_dict[str(edesign_index)] = sub_edesign
edesign_index += 1
combined_edesign = pygsti.protocols.CombinedExperimentDesign(edesigns_dict)
We're now ready to run on the IBM Q processor. We do this using an IBMQExperiment
object, which
First it converts pyGSTi circuits into jobs that can be submitted to IBM Q. This step includes transpiling of the pyGSTi circuits into OpenQASM (and then into QisKit objects).
exp = ibmq.IBMQExperiment(combined_edesign, pspec, circuits_per_batch=75, num_shots=1024)
We're now ready to submit this experiment to IBM Q.
exp.submit(backend)
You can then monitor the jobs. If get an error message, you can query the error using exp['qjob'][i].error_message()
for batch i
.
exp.monitor()
You can then grab the results, Once you see that all the jobs are complete (.retrieve_results()
will just hang if the jobs have not yet completed).
exp.retrieve_results()
This IBMQExperiment
object now contains the results of your experiment. It contains much of the information about exactly what was submitted to IBM Q, and raw results objects that IBM Q returned
print(exp.keys())
But, most importantly, it contains the data formatted into a pyGSTi ProtocolData
object, which is the packaged-up data that pyGSTi analysis proctols use.
data = exp['data']
We can write this data to disk, which saves the ProtocolData
in the standard pyGSTi format. It also pickles (or JSONs) up all of the additional information contained then IBMQExperiment
object, e.g., the job IDs, in a subfolder ibmqexperiment
.
exp.write('test_ibmq_experiment')
If you only want to load the ProtocolData
you can do this using pyGSTi's standard io
functions. We can also load the IBMQExperiment
object, which will skip unpickling any objects when the unpickling fails (e.g., due to changes in QisKit
).
loaded_exp = ibmq.IBMQExperiment.from_dir('test_ibmq_experiment')
Because retrieve_results()
has formatted the data into a ProctocolData
object, we can just hand this to the analysis protocol(s) that are designed for analyzing this type of data. Here we'll analyze this data using a standard RB curve-fitting analysis.
rb = pygsti.protocols.RandomizedBenchmarking(datatype='adjusted_success_probabilities', defaultfit='A-fixed')
results = {}
for key in data.keys():
results[key] = rb.run(data[key])
ws = pygsti.report.Workspace()
ws.init_notebook_mode(autodisplay=True)
for i in data.keys():
print(i)
ws.RandomizedBenchmarkingPlot(results[i])