qml.shadow_expval¶
- shadow_expval(H, k=1, seed=None)[source]¶
Estimate expectation values using Classical Shadows with full differentiability support.
The Classical Shadows protocol provide a way to estimate a large number of expectation values (even non-commuting ones) using a single set of random Pauli measurements. See arXiv:2002.08953 for the original proposal and theoretical details.
- Parameters:
H (Sequence[Operator] | Operator) – Obserable(s) whose expectation values are to be estimated. Provide a single observable or a sequence to estimate the expectation values of multiple observables from the same classical shadows data.
k (int) – Number of equal parts for which to split the shadow’s measurements in order to compute the median of means. The default is
k=1, which simply computes the mean of all measurements.k>1provides no expected advantage for Pauli measurements and Pauli observables.seed (int | None) –
Optional seed for the random Pauli measurement basis in the classical shadows protocol. This controls which bases (X, Y or Z) each qubit is measured in per shot. If
None, a random seed will be generated.Note
The
seedargument only controls the measurement basis choice. Theseedof a simulator device separately controls the sampling outcomes. For fully reproducible results, you must seed both the device and the measurement.dev = qml.device("default.qubit", seed=42, shots=100) @qml.qnode(dev) def circuit(): qml.H(0) return qml.shadow_expval(qml.Z(0), seed=99)
- Returns:
Measurement process instance
- Return type:
See also
This measurement internally relies on the measurement
classical_shadow()and the classClassicalShadowfor post-processing in order to compute expectation values.Example
With the standard
expval()measurement, each group of non-commuting observables requires its own separate circuit execution. However, withshadow_expvalwe can reuse the shadow data generated from the circuit executions to estimate all expectation values simultaneously.Let’s say we want to estimate the expectation values of all three (non-commuting) single qubit Paulis (
X,Y,Z) on a \(| + \rangle\) state. Theoretically, we would expect that \(\langle X \rangle = 1\), \(\langle Y \rangle = \langle Z \rangle = 0\).device = qml.device("default.qubit", seed=42) @qml.set_shots(1_000) @qml.qnode(device) def circuit(): qml.H(0) # Create |+> state return qml.shadow_expval((qml.X(0), qml.Y(0), qml.Z(0)), seed=99)
>>> print(circuit()) [0.984 0. 0.03 ]
This is very close to their expected values!
Differentiability
Consider the following observable,
>>> H = qml.Hamiltonian([1., 1.], [qml.Z(0) @ qml.Z(1), qml.X(0) @ qml.X(1)])
We can estimate its expectation value with the classical shadows protocol:
dev = qml.device("default.qubit", seed=42, wires=range(2)) @qml.set_shots(shots=10_000) @qml.qnode(dev) def circuit(x, obs): qml.Hadamard(0) qml.CNOT((0,1)) qml.RX(x, wires=0) return qml.shadow_expval(obs, seed=99) x = pnp.array(0.5, requires_grad=True)
>>> print(circuit(x, H)) 1.8891 >>> print(qml.grad(circuit)(x, H)) -0.4653...
In
shadow_expval, we can also pass a list of observables to estimate them all from the same shadow data. Note that each qnode execution internally performs one quantum measurement, so be sure to include all observables that you want to estimate from a single measurement in the same execution.>>> Hs = [H, qml.X(0), qml.Y(0), qml.Z(0)] >>> print(circuit(x, Hs)) [ 1.8783 0.0096 -0.0174 0.0138] >>> print(qml.jacobian(circuit)(x, Hs)) [-0.4851 -0.0063 -0.0099 0.0006]