Noise Models from Rigetti Calibration Data on Amazon Braket
Overview
# --- Setup cell added by QCR (not part of the original tutorial) ---
# Source: amazon-braket/amazon-braket-examples @ 0c0818f315479aab9deebed7e7ed7533ac581923, Apache License 2.0.
# Installs the example's dependencies. If a later cell still reports a missing
# package, restart the runtime/kernel and run again from the top.
%pip install -q amazon-braket-sdk==1.117.3 matplotlib pandas
Noise models on Rigetti
This notebook shows how to construct a noise model from device calibration data for Rigetti Cepheus-1-108Q. We compare the measurement outcomes of circuits run on a noisy simulator with the same circuits run on quantum processing units (QPUs), to show that simulating circuits with noise models more closely mimics QPUs.
Before you begin: We recommend being familiar with Noise models on Amazon Braket. Additionally, users should be familiar with Running quantum circuits on QPU devices.
Table of Contents
- Noise model for Rigetti
- Loading device calibration data
- Comparing noisy simulator results to QPU results
- Smaller noise models compared to QPU results
# Use Braket SDK Cost Tracking to estimate the cost to run this example
from braket.tracking import Tracker
t = Tracker().start()import numpy as np
import pandas as pd
from braket.aws import AwsDevice
from braket.circuits import Circuit, Gate
from braket.circuits.noise_model import GateCriteria, NoiseModel, ObservableCriteria
from braket.circuits.noises import (
BitFlip,
Depolarizing,
TwoQubitDepolarizing,
)
from braket.devices import Devices, LocalSimulatorBraket provides access to hardware providers' reported calibration data. This can be used to construct noise models to approximate the behavior of the QPU when running circuits on a noisy simulator. In this tutorial, we focus on local noise models with no crosstalk interactions. Real devices can have crosstalk and unexpected effects that can further degrade the results.
The Cepheus-1-108Q calibration data is available on the Braket devices page. Under qubit specs, the calibration data include the qubit index, with corresponding values for the
One-qubit calibration data (Qubit specs)

Two-qubit calibration data (Edge specs)

We can programmatically access all the calibration data with the Braket SDK. First we load the AwsDevice using the ARN for Rigetti Cepheus-1-108Q.
rigetti = AwsDevice(Devices.Rigetti.Cepheus1108Q)The properties dictionary contains one- and two-qubit calibration data.
one_qubit_data = rigetti.properties.standardized.oneQubitProperties
two_qubit_data = rigetti.properties.standardized.twoQubitPropertiesFor Cepheus-1-108Q, we can get all qubit indices with one_qubit_data.keys() or with rigetti.topology_graph.nodes.
The keys of the two qubit dictionary are the connected qubit pairs separated by a hyphen. For example, if qubit 0 and 1 are connected the key is "0-1".
One-qubit noise
Let's look at the one qubit calibration data for qubit 0.
one_qubit_data["0"]OneQubitProperties(T1=CoherenceTime(value=1.334841857469904e-05, standardError=None, unit='S'), T2=CoherenceTime(value=1.1487766417239698e-05, standardError=None, unit='S'), oneQubitFidelity=[Fidelity1Q(fidelityType=FidelityType(name='RANDOMIZED_BENCHMARKING', description=None), fidelity=0.9964546331080112, standardError=0.00020783764046460049), Fidelity1Q(fidelityType=FidelityType(name='SIMULTANEOUS_RANDOMIZED_BENCHMARKING', description=None), fidelity=0.9964546331080112, standardError=0.00020783764046460049), Fidelity1Q(fidelityType=FidelityType(name='READOUT', description=None), fidelity=0.971, standardError=None)])
For each qubit, there are various metrics of the quality:
-
T1: Thermal relaxation time is related to the time it takes for the excited state, |1⟩, to decay into the ground state, |0⟩. The probability of remaining in the excited state is
-
T2: The dephasing time, is the decay constant for the scale for a |+⟩ state to decohere into the completely mixed state.
-
Fidelity (RB): Single-qubit randomized benchmarking fidelities. RB fidelity quantifies the average gate fidelity where the average is over all Clifford gates. RB describes an effective noise model with gate-independent depolarizing noise on each Clifford gate.
-
Fidelity (sRB): Single-qubit simultaneous randomized benchmarking fidelities. These are extracted by running single-qubit RB on all qubits simultaneously. Note that we expect the sRB fidelity to be lower than standard RB fidelity due to non-local crosstalk type noise on the device.
-
Readout fidelity: Single-qubit readout fidelities describes the probability of a bit flip error before readout in the computational basis. The readout fidelity is related to the probability of correctly measuring the ground state and excited states respectively, e.g.
Now that we know how to extract and use the calibration data, we can build a simple noise model. For every qubit we will add:
- amplitude dampening noise with probability
for every gate - phase dampening noise with probability
for every gate - depolarizing noise with probability
(from simultaneous RB fidelity) for every gate - readout bit flip noise with probability
to measurements
Technically, the sRB fidelity already includes effects from
To create the noise model, we iterate over all qubits keys in one_qubit_data
noise_model = NoiseModel()
# Readout Noise Model
for q, data in rigetti.properties.standardized.oneQubitProperties.items():
try:
readout_error = 1 - data.oneQubitFidelity[2].fidelity # readout
noise_model.add_noise(BitFlip(readout_error), ObservableCriteria(qubits=int(q)))
depolarizing_rate = (
1 - data.oneQubitFidelity[1].fidelity
) # SIMULTANEOUS_RANDOMIZED_BENCHMARKING
noise_model.add_noise(Depolarizing(probability=depolarizing_rate), GateCriteria(qubits=q))
except: # noqa: PERF203
passnum_params = sum(len(item.noise.parameters) for item in noise_model.instructions)
print(f"Number of terms in noise model is: {len(noise_model.instructions)}")
print(f"Number of parameters in noise model is: {num_params}")Number of terms in noise model is: 120 Number of parameters in noise model is: 120
Two-qubit noise
Next we consider adding two-qubit noise to the model.
Let's first look at the data provided in the Cepheus-1-108Q device calibration data. On the first connect, "0-1", the properties are:
two_qubit_data["0-1"]TwoQubitProperties(twoQubitGateFidelity=[GateFidelity2Q(direction=None, gateName='CZ', fidelity=0.9943965178886834, standardError=0.001071379877124974, fidelityType=FidelityType(name='INTERLEAVED_RANDOMIZED_BENCHMARKING', description=None))])
Here, we see the fidelity per gate (CZ) and the associated standard error.
Next we loop over the entries in the two_qubit_data dictionary and add two-qubit depolarizing noise to the model. Notice that Cepheus-1-108Q has symmetric connections ("0-1" and "1-0") so we need to add noise in both directions.
# Two-qubit noise
for pair, data in two_qubit_data.items(): # iterate over qubit connections
# parse strings "0-1" to integers [0, 1]
q0, q1 = (int(s) for s in pair.split("-"))
try:
if data.twoQubitGateFidelity[0].gateName == "CZ":
phase_rate = 1 - data.twoQubitGateFidelity[0].fidelity
noise_model.add_noise(
TwoQubitDepolarizing(phase_rate),
GateCriteria(Gate.CZ, [(q0, q1), (q1, q0)]), # symmetric connections
)
except:
passnum_params = sum(len(item.noise.parameters) for item in noise_model.instructions)
print(f"Number of terms in noise model is: {len(noise_model.instructions)}")
print(f"Number of parameters in noise model is: {num_params}")Number of terms in noise model is: 313 Number of parameters in noise model is: 313
Compare circuits run on device vs simulator with a noise model
Let's just look at the first 5 qubits. Note that to ensure the noise model applied T1 and T2 noise during the time between gate, we manually add identity gates to each moment.
np.random.seed(42)
circ = Circuit().rx(0, 0.5).rz(1, 0.5).rz(2, 0.5).rx(0, np.pi).rx(1, np.pi).rx(2, np.pi).cz(0, 1)
print(circ)T : │ 0 │ 1 │ 2 │
┌──────────┐ ┌──────────┐
q0 : ─┤ Rx(0.50) ├─┤ Rx(3.14) ├───●───
└──────────┘ └──────────┘ │
┌──────────┐ ┌──────────┐ ┌─┴─┐
q1 : ─┤ Rz(0.50) ├─┤ Rx(3.14) ├─┤ Z ├─
└──────────┘ └──────────┘ └───┘
┌──────────┐ ┌──────────┐
q2 : ─┤ Rz(0.50) ├─┤ Rx(3.14) ├───────
└──────────┘ └──────────┘
T : │ 0 │ 1 │ 2 │
noisy_circ = noise_model.apply(circ)
print(noisy_circ)T : │ 0 │ 1 │ 2 │
┌──────────┐ ┌──────────┐ ┌──────────────┐
q0 : ─┤ Rx(0.50) ├─┤ Rx(3.14) ├───●───┤ DEPO(0.0056) ├─
└──────────┘ └──────────┘ │ └──────┬───────┘
┌──────────┐ ┌──────────┐ ┌─┴─┐ ┌──────┴───────┐
q1 : ─┤ Rz(0.50) ├─┤ Rx(3.14) ├─┤ Z ├─┤ DEPO(0.0056) ├─
└──────────┘ └──────────┘ └───┘ └──────────────┘
┌──────────┐ ┌──────────┐
q2 : ─┤ Rz(0.50) ├─┤ Rx(3.14) ├────────────────────────
└──────────┘ └──────────┘
T : │ 0 │ 1 │ 2 │
simulator = LocalSimulator() # noise free simulator
task = simulator.run(circ, shots=10_000)
free_probs = task.result().measurement_probabilitiesnoisy_simulator = LocalSimulator("braket_dm")
noisy_task = noisy_simulator.run(noisy_circ, shots=10_000)
noisy_probs = noisy_task.result().measurement_probabilitiesrigetti_task = rigetti.run(circ, shots=10_000, disable_qubit_rewiring=True)
rigetti_result = rigetti_task.result()
rigetti_probs = rigetti_result.measurement_probabilitiesfree_sim = pd.DataFrame.from_dict(free_probs, orient="index").rename(columns={0: "free_sim"})
noisy_sim = pd.DataFrame.from_dict(noisy_probs, orient="index").rename(columns={0: "noisy_sim"})
Cepheus = pd.DataFrame.from_dict(rigetti_probs, orient="index").rename(columns={0: "Cepheus-1-108Q"})
df = Cepheus.join(noisy_sim).join(free_sim)
df| Cepheus-1-108Q | noisy_sim | free_sim | |
|---|---|---|---|
| 110 | 0.1250 | NaN | NaN |
| 111 | 0.7125 | 0.9326 | 0.9391 |
| 011 | 0.0792 | 0.0649 | 0.0609 |
| 010 | 0.0122 | NaN | NaN |
| 100 | 0.0127 | NaN | NaN |
| 101 | 0.0508 | 0.0009 | NaN |
| 001 | 0.0064 | 0.0016 | NaN |
| 000 | 0.0012 | NaN | NaN |
We can compute the fidelity between the free simulation and Rigetti, as well as the noisy simulation and Rigetti.
def fidelity(p, q):
return np.sum(np.sqrt(p * q))
f_free_Cepheus = fidelity(df["free_sim"], df["Cepheus-1-108Q"])
f_noisy_Cepheus = fidelity(df["noisy_sim"], df["Cepheus-1-108Q"])
print(f"\nTotal fidelity between Cepheus-1-108Q and noise-free simulator is {f_free_Cepheus}")
print(f"\nTotal fidelity between Cepheus-1-108Q and noisy simulator is {f_noisy_Cepheus}")Total fidelity between Cepheus-1-108Q and noise-free simulator is 0.8874405164437654 Total fidelity between Cepheus-1-108Q and noisy simulator is 0.8968109011001625
To better visualize, we can also plot the output probability distributions from each circuit:
import matplotlib.pyplot as plt
%matplotlib inline
df.plot.bar(
title="Comparing noise-free simulator, noisy simulator, and Cepheus-1-108Q",
figsize=(12, 6),
)
text = f"f_free_Cepheus = {f_free_Cepheus:.3f} \nf_noise_model_Cepheus = {f_noisy_Cepheus:.3f}"
plt.text(1, 0.5, text, fontsize=14)
plt.show()We confirm that the simulator with a noise model is closer to the distribution produced by Cepheus-1-108Q.
Smaller, reduced noise models
The full Rigetti Cepheus-1-108Q noise model contains due to non-uniform qubit noise. We can obtain simpler, smaller noise models by coarse graining the model above.
Here, we consider taking the average over all qubits for the
We use the from_filter function to extract all instructions with amplitude dampening noise in the model. We then compute the mean of the error probabilities.
avg_depo = np.mean(
[n.noise.parameters for n in noise_model.from_filter(noise=Depolarizing).instructions],
)
avg_readout = np.mean(
[n.noise.parameters for n in noise_model.from_filter(noise=BitFlip).instructions],
)Now we construct a new noise model with the mean values above:
simple_noise_model = NoiseModel()
simple_noise_model.add_noise(Depolarizing(avg_depo), GateCriteria())
simple_noise_model.add_noise(BitFlip(avg_readout), ObservableCriteria())
print(simple_noise_model)Gate Noise: Depolarizing(0.047140541447784685), GateCriteria(None, None) Readout Noise: BitFlip(0.053861111111111123), ObservableCriteria(None, None)
We can see the resultant circuits contain qubit-independent noise:
simple_noisy_circ = simple_noise_model.apply(circ)
print(simple_noisy_circ)T : │ 0 │ 1 │ ╏
┌──────────┐ ┌─────────────┐ ┌──────────┐ ┌─────────────┐ ╏
q0 : ─┤ Rx(0.50) ├─┤ DEPO(0.047) ├─┤ Rx(3.14) ├─┤ DEPO(0.047) ├─ ╏
└──────────┘ └─────────────┘ └──────────┘ └─────────────┘ ╏
┌──────────┐ ┌─────────────┐ ┌──────────┐ ┌─────────────┐ ╏
q1 : ─┤ Rz(0.50) ├─┤ DEPO(0.047) ├─┤ Rx(3.14) ├─┤ DEPO(0.047) ├─ ╏
└──────────┘ └─────────────┘ └──────────┘ └─────────────┘ ╏
┌──────────┐ ┌─────────────┐ ┌──────────┐ ┌─────────────┐ ╏
q2 : ─┤ Rz(0.50) ├─┤ DEPO(0.047) ├─┤ Rx(3.14) ├─┤ DEPO(0.047) ├─ ╏
└──────────┘ └─────────────┘ └──────────┘ └─────────────┘ ╏
T : │ 0 │ 1 │ ╏
╏
╸╸╸╸╸╸╸╸╸╸╸╸╸╸╸╸╸╸╸╸╸╸╸╸╸╸╸╸╸╸┳╸╸╸╸╸╸╸╸╸╸╸╸╸╸╸╸╸╸╸╸╸╸╸╸╸╸╸╸╸╸╸╸╸╸╸┛
┃
T : │ 2 │ ┃
┌─────────────┐ ┃
q0 : ───●───┤ DEPO(0.047) ├─ ┃
│ └─────────────┘ ┃
┌─┴─┐ ┌─────────────┐ ┃
q1 : ─┤ Z ├─┤ DEPO(0.047) ├─ ┃
└───┘ └─────────────┘ ┃
┃
q2 : ─────────────────────── ┃
┃
T : │ 2 │ ┃
┃
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
We run the circuit on a noisy simulator below
simple_noisy_task = noisy_simulator.run(simple_noisy_circ, shots=100_000)
simple_noisy_probs = simple_noisy_task.result().measurement_probabilitiesand add it to the previous dataframe
simple_noisy_sim = pd.DataFrame.from_dict(simple_noisy_probs, orient="index").rename(
columns={0: "simple_noisy_sim"},
)
df = df.join(simple_noisy_sim)
df| Cepheus-1-108Q | noisy_sim | free_sim | simple_noisy_sim | |
|---|---|---|---|---|
| 110 | 0.1250 | NaN | NaN | 0.04814 |
| 111 | 0.7125 | 0.9326 | 0.9391 | 0.73935 |
| 011 | 0.0792 | 0.0649 | 0.0609 | 0.11805 |
| 010 | 0.0122 | NaN | NaN | 0.00720 |
| 100 | 0.0127 | NaN | NaN | 0.00452 |
| 101 | 0.0508 | 0.0009 | NaN | 0.07078 |
| 001 | 0.0064 | 0.0016 | NaN | 0.01130 |
| 000 | 0.0012 | NaN | NaN | 0.00066 |
We compute the fidelity between the simple noise model and the QPU:
f_simple = fidelity(df["simple_noisy_sim"], df["Cepheus-1-108Q"])
print(f"\nTotal fidelity between Cepheus-1-108Q and full noise model is: {f_noisy_Cepheus}")
print(f"\nTotal fidelity between Cepheus-1-108Q and simple noise model is: {f_simple}")
print(f"\nTotal fidelity between Cepheus-1-108Q and noise-free is: {f_free_Cepheus}")Total fidelity between Cepheus-1-108Q and full noise model is: 0.8968109011001625 Total fidelity between Cepheus-1-108Q and simple noise model is: 0.9863729453631813 Total fidelity between Cepheus-1-108Q and noise-free is: 0.8874405164437654
df.plot.bar(title="Comparing simulators and Cepheus-1-108Q", figsize=(12, 6))
text = f"f_free = {f_free_Cepheus:.3f} \nf_simple_noise_model = {f_simple:.3f} \nf_noise_model = {f_noisy_Cepheus:.3f}"
plt.text(1, 0.5, text, fontsize=14)
plt.show()We see that compared to the full noise model, the simple model is less accurate, however, it is still a significant improvement over the noise-free case and has far fewer parameters in the model.
Summary
In this notebook, we showed how to construct a noise model for Rigetti Cepheus-1-108Q based only on the available calibration data. We used a coarse assumption of gate-independent single-qubit depolarizing noise and gate-dependant two-qubit noise. Our qubit-dependent model could be improved in many ways. We could add gate-dependence noise, or change the depolarizing channel to Pauli channels.
print("Quantum Task Summary")
print(t.quantum_tasks_statistics())
print(
"Note: Charges shown are estimates based on your Amazon Braket simulator and quantum processing unit (QPU) task usage. Estimated charges shown may differ from your actual charges. Estimated charges do not factor in any discounts or credits, and you may experience additional charges based on your use of other services such as Amazon Elastic Compute Cloud (Amazon EC2).",
)
print(
f"Estimated cost to run this example: {t.qpu_tasks_cost() + t.simulator_tasks_cost():.3f} USD",
)Quantum Task Summary
{<_Rigetti.Cepheus1108Q: 'arn:aws:braket:us-west-1::device/qpu/rigetti/Cepheus-1-108Q'>: {'shots': 10000, 'tasks': {'COMPLETED': 1}}}
Note: Charges shown are estimates based on your Amazon Braket simulator and quantum processing unit (QPU) task usage. Estimated charges shown may differ from your actual charges. Estimated charges do not factor in any discounts or credits, and you may experience additional charges based on your use of other services such as Amazon Elastic Compute Cloud (Amazon EC2).
Estimated cost to run this example: 4.550 USD
This entry was created automatically from publicly available records. QCR links to public sources and only stores repository content where the license permits redistribution.
Versions
Cite all versions? Use the base QCR ID to always reference the latest version of this entry.
Join the Discussion
Comments (0)
No comments yet. Be the first to share your thoughts!