Robust shadows
Robust shadows 2 were built upon the classical shadow scheme but have the particularity to be noise-resilient. It involves an experimental simple calibration procedure based on the preparation of a all-zero state with very high fidelity. We then perform noisy randomized measurements to learn about the averaged, as known as twirled
, effect of the noise, obtaining calibration coefficients for shadows. One can efficiently characterize and mitigate noises in the shadow estimation scheme, given only minimal assumptions on the experimental conditions. Such a procedure has been used in 4 to estimate the Quantum Fisher information out of a quantum system. Note that robust shadows are equivalent to classical shadows in non-noisy settings by setting the calibration
coefficients to \(\frac{1}{3}\) for each qubit, as shown below:
from qadence_measurement.calibration import zero_state_calibration
# Classical shadows are defined up to some accuracy and confidence.
from qadence_measurement.utils.data_acquisition import number_of_samples
shadow_options = {"accuracy": 0.1, "confidence": 0.1}
N, K = number_of_samples(observable, shadow_options["accuracy"], shadow_options["confidence"])
# Calibration coefficients are by default 1/3
calibration = zero_state_calibration(N, n_qubits=2, n_shots=100, backend=model.backend, noise=None)
robust_shadow_options = {"shadow_size": N, "shadow_medians": K, "calibration": calibration}
robust_shadow_measurement = Measurements(protocol=MeasurementProtocol.ROBUST_SHADOW, options=robust_shadow_options)
estimated_values_robust_shadow = robust_shadow_measurement(model=model)
Robust shadow tomography
In this tutorial, we will estimate a physical property out of a quantum system, namely the purity of the partial traces, in the presence of measurement noise. To do so, we will use the formalism of classical shadows1, and especially their robust version2. This tutorial is inspired from a notebook example of robust shadow tomography from the randomized measurements toolbox5 in Julia3.
Setting the model
First, we will set the noise model and a circuit from which we will estimate the purity.
Noise model
We will use a depolarizing noise model with a different error probability per qubit.
import torch
from qadence import NoiseHandler, NoiseProtocol
torch.manual_seed(0)
n_qubits = 2
error_probs = torch.clamp(0.1 + 0.02 * torch.randn(n_qubits), min=0, max=1)
noise = NoiseHandler(protocol=NoiseProtocol.DIGITAL.DEPOLARIZING, options={"error_probability": error_probs[0], "target": 0})
for i, proba in enumerate(error_probs[1:]):
noise.digital_depolarizing(options={"error_probability": proba, "target": i+1})
Noiseless circuit and model
Let us set the circuit without noise and calculating the expected purities:
from qadence import *
theta1 = Parameter("theta1", trainable=False)
theta2 = Parameter("theta2", trainable=False)
theta3 = Parameter("theta3", trainable=False)
theta4 = Parameter("theta4", trainable=False)
blocks = chain(
kron(RX(0, theta1), RY(1, theta2)),
kron(RX(0, theta3), RY(1, theta4,),),
)
circuit = QuantumCircuit(2, blocks)
values = {
"theta1": torch.tensor([0.5]),
"theta2": torch.tensor([1.5]),
"theta3": torch.tensor([2.0]),
"theta4": torch.tensor([2.5]),
}
# no observable needed here
model = QuantumModel(
circuit=circuit,
)
For calculating purities, we can use the utility functions partial_trace
and purity
:
from qadence_measurement.utils.utils_trace import partial_trace, purity
def partial_purities(density_mat):
purities = []
for i in range(n_qubits):
partial_trace_i = partial_trace(density_mat, [i]).squeeze()
purities.append(purity(partial_trace_i))
return torch.tensor(purities)
expected_purities = partial_purities(model.run(values))
Add noise to circuit
The circuit is defined as follows where we set the previous noise model in the last operations as measurement noise.
noisy_blocks = chain(
kron(RX(0, theta1), RY(1, theta2)),
kron(RX(0, theta3, NoiseHandler(protocol=NoiseProtocol.DIGITAL.DEPOLARIZING, options={"error_probability": error_probs[0], "target": 0})),
RY(1, theta4, NoiseHandler(protocol=NoiseProtocol.DIGITAL.DEPOLARIZING, options={"error_probability": error_probs[1], "target": 1})),
),
)
noisy_circuit = QuantumCircuit(2, noisy_blocks)
noisy_model = QuantumModel(
circuit=noisy_circuit,
)
Shadow estimations
Vanilla classical shadows
We will first run vanilla shadows to reconstruct the density matrix representation of the circuit, from which we can estimate the purities.
from qadence_measurement.protocol import Measurements
from qadence_measurement.utils.types import MeasurementProtocol
shadow_options = {"shadow_size": 10200, "shadow_medians": 6, "n_shots":1000}
shadow_measurements = Measurements(protocol=MeasurementProtocol.SHADOW, options=shadow_options)
shadow_measurements.measure(noisy_model, param_values=values)
vanilla_purities = partial_purities(shadow_measurements.reconstruct_state())
As we can see, the estimated purities diverge from the expected ones due to the presence of noise. Next, we will use robust shadows to mitigate the noise effect.
Calibration for Robust shadows
We now use an efficient calibration method based on the experimental demonstration of classical shadows4. A first set of measurements are used to determine calibration coefficients. The latter are used within robust shadows to mitigate measurement errors. Indeed, we witness below the estimated purities being closer to the analytical ones.
from qadence_measurement.calibration import zero_state_calibration
calibration = zero_state_calibration(n_unitaries=2000, n_qubits=circuit.n_qubits, n_shots=10000, noise=noise)
robust_options = {"shadow_size": 10200, "shadow_medians": 6, "n_shots":1000, "calibration": calibration}
robust_shadow_measurements = Measurements(protocol=MeasurementProtocol.ROBUST_SHADOW, options=robust_options)
robust_shadow_measurements.measure(noisy_model, param_values=values)
robust_purities = partial_purities(robust_shadow_measurements.reconstruct_state())
-
Hsin-Yuan Huang, Richard Kueng and John Preskill, Predicting Many Properties of a Quantum System from Very Few Measurements (2020) ↩
-
Senrui Chen, Wenjun Yu, Pei Zeng, and Steven T. Flammia, Robust Shadow Estimation (2021) ↩↩
-
Vittorio Vitale, Aniket Rath, Petar Jurcevic, Andreas Elben, Cyril Branciard, and Benoît Vermersch, Robust Estimation of the Quantum Fisher Information on a Quantum Processor (2024) ↩↩
-
Andreas Elben, Steven T. Flammia, Hsin-Yuan Huang, Richard Kueng, John Preskill, Benoît Vermersch, and Peter Zoller, The randomized measurement toolbox (2022) ↩