Interleaved Randomized Benchmarking (IRB)
The interleaved randomized benchmarking routine allows us to estimate the fidelity of single and two qubit Clifford operations. To demonstrate this routine, consider device noise modelled by an amplitude damping channel with decay probability \(\gamma=0.05\). We want to estimate the fidelity of the single-qubit \(Z\) gate.
[1]:
import cirq
import numpy as np
import supermarq
[2]:
decay_prob = 0.05
noise = cirq.AmplitudeDampingChannel(gamma=decay_prob)
simulator = cirq.DensityMatrixSimulator(noise=noise, seed=0)
We can calculate the average fidelity of this channel as:
Thus we have a gate error
[3]:
expected_gate_error = 1 / 3 + decay_prob / 6 - np.sqrt(1 - decay_prob) / 3
Now we run the experiments and analyse the results.
[4]:
experiment = supermarq.qcvv.IRB(150, [2, 6, 15, 20, 30], interleaved_gate=cirq.Z, random_seed=0)
result = experiment.run_with_simulator(simulator=simulator)
[5]:
result.analyze()
Estimated gate error: 0.013100 +/- 0.003058
[6]:
print(f"Expected gate error: {expected_gate_error:.6f}")
print(
f"Measured gate error: {result.average_interleaved_gate_error:.6f} +/- {result.average_interleaved_gate_error_std:.6f}"
)
Expected gate error: 0.016774
Measured gate error: 0.013100 +/- 0.003058
So our estimate is in good agreement with the expected result.
Two Qubit RB
The IRB() routine can also perform vanilla Randomised Benchmarking (RB) by instead passing interleaved_gate=None. We demonstrate this with a two qubit depolarising noise model with an error of \(0.02\).
First we have to define a custom noise model.
[7]:
class TwoQubitDepolariseNoiseModel(cirq.NoiseModel):
"""Applies two qubit depolarising channels"""
def __init__(self, average_qubit_error: float) -> None:
"""Args:
average_qubit_error: The average two qubit error rate
"""
super().__init__()
# Note the conversion from average error to pauli error which is used by the cirq channel.
self.two_qubit_pauli_error = 15 / 16 * 4 / 3 * average_qubit_error
self.two_qubit_depolarise = cirq.DepolarizingChannel(
p=self.two_qubit_pauli_error, n_qubits=2
)
def noisy_operation(self, operation: cirq.Operation) -> list[cirq.OP_TREE]:
"""Produces a list of operations by applying each noise model
to the provided operation depending on the number of qubits it acts on.
"""
if len(operation.qubits) == 2:
return [operation, self.two_qubit_depolarise(*operation.qubits)]
return [operation]
two_qubit_noise = TwoQubitDepolariseNoiseModel(two_qubit_error := 0.02)
two_qubit_simulator = cirq.DensityMatrixSimulator(noise=two_qubit_noise, seed=0)
Now run the experiment and analyse the results
[8]:
experiment2 = supermarq.qcvv.IRB(
50, [2, 6, 15, 20, 30], interleaved_gate=None, num_qubits=2, random_seed=0
)
results2 = experiment2.run_with_simulator(simulator=two_qubit_simulator)
[9]:
results2.analyze()
Estimated error per Clifford: 0.032233 +/- 0.002016
[10]:
gpc = experiment2.gates_per_clifford()
print(f"Expected average gate error: {two_qubit_error:.6f}")
print(
f"Measured average gate error: {results2.average_error_per_clifford / gpc['two_qubit_gates']:.6f} +/- {results2.average_error_per_clifford_std / gpc['two_qubit_gates']:.6f}"
)
Expected average gate error: 0.020000
Measured average gate error: 0.021749 +/- 0.001360
Note that because randomised benchmarking measures the error per Clifford operation, we have to take into account how many two qubit gates are used on average to implement a two qubit Clifford operation.
We see that the measured two qubit gate error is in good agreement with the true value used by the simulator.