Skip to content

Clifford Mirror RB experiment #1090

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 63 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
921c185
add mirror RB experiment and analysis files
albertzhu01 Jul 11, 2022
9cd7074
edit appropriate mirrorRB-relevant files for PR
albertzhu01 Jul 11, 2022
2de778b
remove prints and add paper ref
paco-ri Jul 12, 2022
a28e6d2
update docstrings
albertzhu01 Jul 11, 2022
9d440e8
delete commented series code
albertzhu01 Jul 11, 2022
8d656b6
fix pygsti version
albertzhu01 Jul 12, 2022
b0432fd
remove outcomes setting
albertzhu01 Jul 12, 2022
e1de0e4
cleaned RB tutorial
paco-ri Jul 12, 2022
859a94e
move coupling map outside loop, delete elements_without_inv and int_c…
albertzhu01 Jul 12, 2022
c1444ae
reformat files with black and lint
albertzhu01 Jul 12, 2022
6857a48
moved Clifford sampling in edgegrab algo
paco-ri Jul 12, 2022
8445f4a
use full connectivity for coupling map if coupling map is not provided
albertzhu01 Aug 8, 2022
63f171c
changed AnalysisResult imports
paco-ri Sep 1, 2022
bbf111f
edited BitGenerator and SeedSequence imports
paco-ri Sep 7, 2022
2308072
removed MirrorRBPyGSTi from library init file
paco-ri Sep 7, 2022
ebfa4d6
fix full connectivity map generation code
albertzhu01 Oct 31, 2022
de33876
remove MirrorRBPyGSTi from mirror_rb_experiment.py, rb tutorial, and …
albertzhu01 Oct 31, 2022
6fccca1
fix coupling map full connectivity generation and update aer imports …
albertzhu01 Nov 1, 2022
d2f8ff9
reformat imports (temporarily) to pass lint with merged code from ups…
albertzhu01 Nov 3, 2022
a7b7a70
merged main branch
coruscating Jan 31, 2023
20b36db
added edge grab docstring
coruscating Feb 24, 2023
c8c8a7b
moved sampler to its own file
coruscating Feb 25, 2023
ea53a5d
updated edgegrab
coruscating Feb 28, 2023
07b4b28
coupling map
coruscating Feb 28, 2023
31f565e
updated release note
coruscating Mar 2, 2023
41f9e81
partial refactoring
coruscating Mar 11, 2023
1f58ffb
fix rebase errors
coruscating Mar 11, 2023
961068d
update docs and remove edgegrab from utils
coruscating Mar 14, 2023
7d79339
changed 1Q from integer to clifford
coruscating Mar 15, 2023
e82df72
merge main
coruscating Mar 15, 2023
e3c9b61
fixed circuit length
coruscating Mar 16, 2023
e66a58d
fix rng and force default transpile
coruscating Mar 16, 2023
52ce46b
fix bugs to pass tests
coruscating Mar 17, 2023
f06dbc9
restored some RB changes and fixed tests
coruscating Mar 17, 2023
e69701d
updated manual
coruscating Mar 17, 2023
e6f8a07
replaced missing function
coruscating Mar 19, 2023
7651798
rewrote circuit generation logic and sampler
coruscating Mar 21, 2023
400a993
update 2q+ code
coruscating Mar 21, 2023
97a746d
fixed wrong indexing for general coupling maps
coruscating Mar 22, 2023
b32d331
lint and improve manual
coruscating Mar 22, 2023
d589aa6
Merge remote-tracking branch 'upstream/main' into mirrorrb-rebased
coruscating Mar 22, 2023
c70e150
update manual
coruscating Mar 22, 2023
a6c8ff8
refactored sampling utils and added tests
coruscating Mar 22, 2023
999bd6f
added distribution property to `MirrorRB`
coruscating Mar 23, 2023
3407385
merged main
coruscating Mar 23, 2023
3b0ae70
lint and fix docs
coruscating Mar 23, 2023
d318e51
fix doc links
coruscating Mar 23, 2023
530e5b6
merge main
coruscating Mar 23, 2023
fb23e99
Update tests
coruscating Mar 23, 2023
7224f59
fix nonlocal test
coruscating Mar 23, 2023
2a0f0c2
Merge remote-tracking branch 'upstream/main' into mirrorrb-rebased
coruscating Mar 24, 2023
09fe450
address review comments
coruscating Mar 24, 2023
4951cb1
change sampler output to list of named tuple
coruscating Mar 26, 2023
585c053
change gate distribution to named tuple
coruscating Mar 27, 2023
c209d1e
merge main
coruscating Mar 27, 2023
3812049
refactored experiment options
coruscating Mar 27, 2023
3070431
Merge remote-tracking branch 'upstream/main' into mirrorrb-rebased
coruscating Mar 27, 2023
d423e58
subclass`MirrorRBAnalysis` from `RBAnalysis`
coruscating Mar 27, 2023
b71d098
change y-axis option to `analyzed_quantity`
coruscating Mar 28, 2023
0510478
changed to `CouplingMap` type and added validation
coruscating Mar 28, 2023
1c98049
added data processor node for analysis
coruscating Mar 28, 2023
fb41d6d
change sampler to return a generator
coruscating Mar 28, 2023
7710f7c
Address review comments and fix tests
coruscating Mar 28, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
nbsphinx_thumbnails = {
"manuals/verification/quantum_volume": "_images/quantum_volume_2_0.png",
"manuals/measurement/readout_mitigation": "_images/readout_mitigation_4_0.png",
"manuals/verification/mirror_rb": "_images/mirror_rb_1_0.png",
"manuals/verification/randomized_benchmarking": "_images/randomized_benchmarking_3_1.png",
"manuals/measurement/restless_measurements": "_images/restless_shots.png",
"manuals/verification/state_tomography": "_images/state_tomography_3_0.png",
Expand Down Expand Up @@ -159,6 +160,7 @@
intersphinx_mapping = {
"matplotlib": ("https://matplotlib.org/stable/", None),
"qiskit": ("https://qiskit.org/documentation/", None),
"pygsti": ("https://pygsti.readthedocs.io/en/latest/", None),
"uncertainties": ("https://pythonhosted.org/uncertainties", None),
"qiskit_ibm_provider": ("https://qiskit.org/documentation/partners/qiskit_ibm_provider", None),
}
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
286 changes: 286 additions & 0 deletions docs/manuals/verification/mirror_rb.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,286 @@
Mirror Randomized Benchmarking
==============================

Mirror randomized benchmarking (mirror RB) is a randomized benchmarking protocol
where layers of gates are sampled from a distribution and then run on a set of
qubits along with their mirror inverses. A randomized Clifford mirror circuit
[1]_, which we will be running in this manual, is a specific type of mirror RB
and consists of:

- random n-qubit Clifford layers and their inverses sampled according to some
distribution :math:`\Omega` over a layer set :math:`\mathbb{L}`,

- uniformly random one-qubit Paulis between these layers, and

- a layer of uniformly random one-qubit Cliffords at the beginning and the end
of the circuit.

Note that the random n-qubit Clifford layers can be realized with only one-qubit
Cliffords and a two-qubit gate such as CX, which twirl the local errors
sufficiently to produce a useful metric of gate infidelity. This is in contrast
to standard RB, which requires the implementation of n-qubit Cliffords that have
much more overhead for large n. As a result, mirror RB is more scalable than
standard RB and is suitable for characterizing crosstalk errors over a large
number of qubits in a quantum device. Mirror RB can also be generalized to
universal gatesets beyond the Cliffords [2]_.

Output metrics
--------------

In standard and interleaved RB, :math:`n`-qubit circuits of varying lengths :math:`\ell`
that compose to the identity are run on a device, and the **success probability**
:math:`P`, the probability that the circuit's output bit string equals the input bit
string, is estimated for each circuit length by running several circuits at each length.
The :math:`P`-versus-:math:`\ell` curve is fit to the function :math:`A\alpha^\ell + b`,
and the error per Clifford (EPC) (the average infidelity) is estimated using

.. math::

r = \frac{\left(2^n - 1\right)p}{2^n}.

Our implementation of MRB computes additional values in addition to the
success probability that have been seen in the literature and ``pyGSTi``.
Specifically, we compute the **adjusted success probability**

.. math::

P_0 = \sum_{k=0}^n \left(-\frac{1}{2}\right)^k h_k,

where :math:`h_k` is the probability of the actual output bit string being Hamming
distance :math:`k` away from the expected output bit string (note :math:`h_0 = P`). We
also compute the **effective polarization**, which is fitted and visualized by default:

.. math::

S = \frac{4^n P_0}{4^n - 1} - \frac{1}{4^n - 1}.

In [1]_, the function :math:`A\alpha^\ell` (without a baseline) is fit to the
effective polarizations to find entanglement infidelities.

In Qiskit Experiments, mirror RB analysis results include the following:

- ``alpha``: the depolarizing parameter. The user can select which of :math:`P, P_0, S`
to fit, and the corresponding :math:`\alpha` will be provided.

- ``EPC``: the expectation of the average gate infidelity of a layer sampled
according to :math:`\Omega`.

- ``EI``: the expectation of the entanglement infidelity of a layer sampled
according to :math:`\Omega`.

Note that the ``EPC`` :math:`\epsilon_a` and the ``EI`` :math:`\epsilon_e` are
related by

.. math::

\epsilon_e = \left(1 + \frac{1}{2^n}\right) \epsilon_a,

where :math:`n` is the number of qubits (see [2]_).


Running a mirror RB experiment
------------------------------

The distribution for sampling layers, :math:`\Omega`, must be specified by the user when
instantiating a mirror RB experiment. A commonly used :math:`\Omega` is one generated by
the **edge grab** algorithm [3]_. The Clifford layers in :math:`\mathbb{L}` are
constructed from a gate set consisting of one-qubit Clifford gates and a single
two-qubit Clifford gate (e.g., CX) that can be applied to any two connected qubits. The
user can specify an expected two-qubit gate density :math:`\xi \in \left[0,
\frac{1}{2}\right]`, and each intermediate Clifford layer will have approximately
:math:`n \xi` CXs on average.

Even though a :class:`.MirrorRB` experiment can be instantiated without a
backend, the backend must be specified when the circuits are sampled because
sampling algorithms containing two-qubit gates need to know the backend's
connectivity. To use your own :math:`\Omega`, you must implement your own
subclass of the abstract :class:`.BaseSampler` class, but in this manual we will
use the built-in :class:`.EdgeGrabSampler`. Here's how to instantiate and run
the experiment:

.. jupyter-execute::

import numpy as np
from qiskit_experiments.library import MirrorRB
from qiskit_experiments.library.randomized_benchmarking.sampling_utils import EdgeGrabSampler

from qiskit_aer import AerSimulator
from qiskit.providers.fake_provider import FakeParisV2

backend = AerSimulator.from_backend(FakeParisV2())

lengths = np.arange(2, 810, 200)
num_samples = 5
seed = 1010
qubits = (0,1)

exp_2q = MirrorRB(qubits, lengths, backend=backend, num_samples=num_samples, seed=seed)
expdata_2q = exp_2q.run(backend).block_for_results()
results_2q = expdata_2q.analysis_results()

.. jupyter-execute::

display(expdata_2q.figure(0))
for result in results_2q:
print(result)

Selecting the analyzed quantity
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

You can set what you want to use as the metric for fitting by setting the
``analyzed_quantity`` analysis option. Here's an example of plotting the success
probability instead of the default:

.. jupyter-execute::

lengths = np.arange(2,202,50)
num_samples = 5
seed = 42
qubits = (0,)

exp = MirrorRB(qubits, lengths, backend=backend, num_samples=num_samples, seed=seed)

# select analyzed_quantity, can also be "Adjusted Success Probability" or "Effective Polarization"
exp.analysis.set_options(analyzed_quantity="Success Probability")

# y-axis label must be set separately
exp.analysis.options.plotter.set_figure_options(
ylabel="Success Probability",
)
expdata = exp.run(backend).block_for_results()
results = expdata.analysis_results()

.. jupyter-execute::

display(expdata.figure(0))
for result in results:
print(result)


Mirror RB user options
~~~~~~~~~~~~~~~~~~~~~~

There are several options that change the composition of the mirror RB circuit layers.

- ``pauli_randomize`` (default ``True``): if ``True``, put layers of uniformly
random Paulis between the intermediate sampled layers

- ``start_end_clifford`` (default ``True``): if ``True``, begin the circuit with
uniformly random one-qubit Cliffords and end the circuit with their inverses

- ``inverting_pauli_layer`` (default ``False``): if ``True``, add a layer of
Paulis at the end of the circuit to set the output to
:math:`\left\vert0\right\rangle^{\otimes n}`, up to a global phase

The default settings produce the circuits in Ref. [1]_.

Let's look at how these options change the circuit. First, the default with Pauli layers
between Cliffords and single-qubit Cliffords at the start and end:

.. jupyter-execute::

exp = MirrorRB((0,1,2),
lengths=[2],
seed=100,
backend=backend,
num_samples=1)
exp.circuits()[0].decompose().remove_final_measurements(inplace=False).draw("mpl")

And now with the start and end Clifford layers turned off and the inverting Pauli layer added at the end:

.. jupyter-execute::

exp = MirrorRB((0,1,2),
lengths=[2],
seed=100,
backend=backend,
num_samples=1,
start_end_clifford=False,
pauli_randomize=True,
inverting_pauli_layer=True)
exp.circuits()[0].decompose().remove_final_measurements(inplace=False).draw("mpl")

Another important option is ``two_qubit_gate_density`` (default ``0.2``). This is the
expected fraction of two-qubit gates in the circuit, not accounting for the optional
constant number of Clifford and Pauli layers at the start and end. This means that given
the same ``two_qubit_gate_density``, if ``pauli_randomize`` is off, the concentration of
two-qubit gates in the Clifford layers will be halved so that the overall density doesn't
change. We'll demonstrate this by first leaving ``pauli_randomize`` on:

.. jupyter-execute::

# choose a linear string on this backend for ease of visualization
exp = MirrorRB((0,1,2,3,5,8,11,14),
lengths=[2],
two_qubit_gate_density=0.5,
seed=100,
backend=backend,
num_samples=1,
start_end_clifford=False)
exp.circuits()[0].remove_final_measurements(inplace=False).draw("mpl")

And now we remove the Pauli layers to see that the CX density in the Clifford layers
has decreased:

.. jupyter-execute::

exp = MirrorRB((0,1,2,3,5,8,11,14),
lengths=[2],
two_qubit_gate_density=0.5,
pauli_randomize=False,
seed=100,
backend=backend,
num_samples=1,
start_end_clifford=False)
exp.circuits()[0].remove_final_measurements(inplace=False).draw("mpl")

Note that the edge grab algorithm is probabilistic and only tends to the exact two
qubit gate density asymptotically, so you may not get layers fully packed with
two-qubit gates even if you specify a density of 1.

The default interface of :class:`MirrorRB` only allows the above options in
addition to passing ``sampler_opts`` to your sampler upon instantiation. If you
decide to choose a custom gate set or implement your own sampler, note that the
validity of fitting the effective polarizaton as a function of circuit length to
obtain average gate infidelity depends on a list of assumptions [1]_ that may no
longer be valid.

Mirror RB implementation in ``pyGSTi``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The :mod:`pygsti` implementation of mirror RB,
:class:`~.pygsti.protocols.rb.MirrorRBDesign`, can be used for testing and comparison.
We note however that ``pyGSTi`` transpiles circuits slightly differently, producing
small discrepancies in fit parameters between the two codes. To illustrate, consider the
two circuits below, both of which were generated in ``pyGSTi``. This first circuit was
transpiled in ``pyGSTi``:

.. image:: images/pygsti-data-pygsti-transpiled-circ.png

This second circuit was transpiled in Qiskit:

.. image:: images/pygsti-data-qiskit-transpiled-circ.png

Note the different implementations of the same Clifford on
qubit 0 in the fifth layer.

References
----------

.. [1] Timothy Proctor, Stefan Seritan, Kenneth Rudinger, Erik Nielsen, Robin
Blume-Kohout, Kevin Young, *Scalable randomized benchmarking of quantum
computers using mirror circuits*, https://arxiv.org/pdf/2112.09853.pdf

.. [2] Hines, Jordan, et al. *Demonstrating scalable randomized benchmarking of
universal gate sets*, https://arxiv.org/abs/2207.07272

.. [3] Timothy Proctor, Kenneth Rudinger, Kevin Young, Erik Nielsen, and Robin
Blume-Kohout, *Measuring the Capabilities of Quantum Computers*,
https://arxiv.org/pdf/2008.11294.pdf


See also
--------

* API documentation: :mod:`.MirrorRB`
* Experiment manual: :doc:`/manuals/verification/randomized_benchmarking`
3 changes: 2 additions & 1 deletion qiskit_experiments/library/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@

~randomized_benchmarking.StandardRB
~randomized_benchmarking.InterleavedRB
~randomized_benchmarking.MirrorRB
~tomography.TomographyExperiment
~tomography.StateTomography
~tomography.ProcessTomography
Expand Down Expand Up @@ -183,7 +184,7 @@ class instance to manage parameters and pulse schedules.
ZZRamsey,
MultiStateDiscrimination,
)
from .randomized_benchmarking import StandardRB, InterleavedRB
from .randomized_benchmarking import StandardRB, InterleavedRB, MirrorRB
from .tomography import (
TomographyExperiment,
StateTomography,
Expand Down
12 changes: 12 additions & 0 deletions qiskit_experiments/library/randomized_benchmarking/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

StandardRB
InterleavedRB
MirrorRB


Analysis
Expand All @@ -36,15 +37,26 @@

RBAnalysis
InterleavedRBAnalysis
MirrorRBAnalysis

Utilities
=========

.. autosummary::
:toctree: ../stubs/

RBUtils
BaseSampler
EdgeGrabSampler
SingleQubitSampler

"""
from .standard_rb import StandardRB
from .interleaved_rb_experiment import InterleavedRB
from .mirror_rb_experiment import MirrorRB
from .sampling_utils import BaseSampler, EdgeGrabSampler, SingleQubitSampler
from .rb_analysis import RBAnalysis
from .interleaved_rb_analysis import InterleavedRBAnalysis
from .mirror_rb_analysis import MirrorRBAnalysis
from .clifford_utils import CliffordUtils
from .rb_utils import RBUtils
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,18 @@ def _unpack_num(num, sig):
return res


def compute_target_bitstring(circuit: QuantumCircuit) -> str:
"""For a Pauli circuit C, which consists only of Clifford gates, compute C|0>.
Args:
circuit: A Pauli QuantumCircuit.
Returns:
Target bitstring.
"""
# target string has a 1 for each True in the stabilizer half of the phase vector
target = "".join(["1" if phase else "0" for phase in Clifford(circuit).stab_phase[::-1]])
return target


# Constant mapping from 1Q single Clifford gate to 1Q Clifford numerical identifier.
# This table must be generated using `data.generate_clifford_data.gen_cliff_single_1q_gate_map`, or,
# equivalently, correspond to the ordering implicitly defined by CliffUtils.clifford_1_qubit_circuit.
Expand Down
Loading