qml.ClassicalShadow¶
- class ClassicalShadow(bits, recipes, wire_map=None)[source]¶
Bases:
object
Class for classical shadow post-processing expectation values, approximate states, and entropies.
A
ClassicalShadow
is a classical description of a quantum state that is capable of reproducing expectation values of local Pauli observables, see 2002.08953.The idea is to capture \(T\) local snapshots (given by the
shots
set in the device) of the state by performing measurements in random Pauli bases at each qubit. The measurement outcomes, denotedbits
, as well as the choices of measurement bases,recipes
, are recorded in two(T, len(wires))
integer tensors, respectively.From the \(t\)-th measurement, we can reconstruct the
local_snapshots
(see methods)\[\rho^{(t)} = \bigotimes_{i=1}^{n} 3 U^\dagger_i |b_i \rangle \langle b_i | U_i - \mathbb{I},\]where \(U_i\) is the rotation corresponding to the measurement (e.g. \(U_i=H\) for measurement in \(X\)) of qubit \(i\) at snapshot \(t\) and \(|b_i\rangle = (1 - b_i, b_i)\) the corresponding computational basis state given the output bit \(b_i\).
From these local snapshots, one can compute expectation values of local Pauli strings, where locality refers to the number of non-Identity operators. The accuracy of the procedure is determined by the number of measurements \(T\) (
shots
). To target an error \(\epsilon\), one needs of order \(T = \mathcal{O}\left( \log(M) 4^\ell/\epsilon^2 \right)\) measurements to determine \(M\) different, \(\ell\)-local observables.One can in principle also reconstruct the global state \(\sum_t \rho^{(t)}/T\), though it is not advisable nor practical for larger systems due to its exponential scaling.
Note
As per arXiv:2103.07510, when computing multiple expectation values it is advisable to directly estimate the desired observables by simultaneously measuring qubit-wise-commuting terms. One way of doing this in PennyLane is via
Hamiltonian
and settinggrouping_type="qwc"
. For more details on this topic, see our demo on estimating expectation values with classical shadows.- Parameters
bits (tensor) – recorded measurement outcomes in random Pauli bases.
recipes (tensor) – recorded measurement bases.
wire_map (list[int]) – list of the measured wires in the order that they appear in the columns of
bits
andrecipes
. If None, defaults torange(n)
, wheren
is the number of measured wires.
See also
Demo on Estimating observables with classical shadows in the Pauli basis,
classical_shadow()
Example
We obtain the
bits
andrecipes
viaclassical_shadow()
measurement:dev = qml.device("default.qubit", wires=range(2), shots=1000) @qml.qnode(dev) def qnode(x): qml.Hadamard(0) qml.CNOT((0,1)) qml.RX(x, wires=0) return qml.classical_shadow(wires=range(2)) bits, recipes = qnode(0) shadow = qml.ClassicalShadow(bits, recipes)
After recording these
T=1000
quantum measurements, we can post-process the results to arbitrary local expectation values of Pauli strings. For example, we can compute the expectation value of a Pauli string>>> shadow.expval(qml.X(0) @ qml.X(1), k=1) array(0.972)
or of a Hamiltonian:
>>> H = qml.Hamiltonian([1., 1.], [qml.Z(0) @ qml.Z(1), qml.X(0) @ qml.X(1)]) >>> shadow.expval(H, k=1) array(1.917)
The parameter
k
is used to estimate the expectation values via the median of means algorithm (see 2002.08953). The casek=1
corresponds to simply taking the mean value over all local snapshots.k>1
corresponds to splitting theT
local snapshots intok
equal parts, and taking the median of their individual means. For the case of measuring only in the Pauli basis, there is no advantage expected from settingk>1
.Attributes
The number of snapshots in the classical shadow measurement.
- snapshots¶
The number of snapshots in the classical shadow measurement.
Methods
entropy
(wires[, snapshots, alpha, k, base])Compute entropies from classical shadow measurements.
expval
(H[, k])Compute expectation value of an observable \(H\).
global_snapshots
([wires, snapshots])Compute the T x 2**n x 2**n global snapshots
local_snapshots
([wires, snapshots])Compute the T x n x 2 x 2 local snapshots
- entropy(wires, snapshots=None, alpha=2, k=1, base=None)[source]¶
Compute entropies from classical shadow measurements.
Compute general Renyi entropies of order \(\alpha\) for a reduced density matrix \(\rho\) in terms of
\[S_\alpha(\rho) = \frac{1}{1-\alpha} \log\left(\text{tr}\left[\rho^\alpha \right] \right).\]There are two interesting special cases: In the limit \(\alpha \rightarrow 1\), we find the von Neumann entropy
\[S_{\alpha=1}(\rho) = -\text{tr}(\rho \log(\rho)).\]In the case of \(\alpha = 2\), the Renyi entropy becomes the logarithm of the purity of the reduced state
\[S_{\alpha=2}(\rho) = - \log\left(\text{tr}(\rho^2) \right).\]Since density matrices reconstructed from classical shadows can have negative eigenvalues, we use the algorithm described in 1106.5458 to project the estimator to the closest valid state.
Warning
Entropies are non-linear functions of the quantum state. Accuracy bounds on entropies with classical shadows are not known exactly, but scale exponentially in the subsystem size. It is advisable to only compute entropies for small subsystems of a few qubits. Further, entropies as post-processed by this class method are currently not automatically differentiable.
- Parameters
wires (Iterable[int]) – The wires over which to compute the entropy of their reduced state. Note that the computation scales exponentially in the number of wires for the reduced state.
snapshots (Iterable[int] or int) – Only compute a subset of local snapshots. For
snapshots=None
(default), all local snapshots are taken. In case of an integer, a random subset of that size is taken. The subset can also be explicitly fixed by passing an Iterable with the corresponding indices.alpha (float) – order of the Renyi-entropy. Defaults to
alpha=2
, which corresponds to the purity of the reduced state. This case is straight forward to compute. All other casesalpha!=2
necessitate computing the eigenvalues of the reduced state and thus may lead to longer computations times. Another special case isalpha=1
, which corresponds to the von Neumann entropy.k (int) – Allow to split the snapshots into
k
equal parts and estimate the snapshots in a median of means fashion. There is no known advantage to do this for entropies. Thus,k=1
is default and advised.base (float) – Base to the logarithm used for the entropies.
- Returns
Entropy of the chosen subsystem.
- Return type
float
Example
For the maximally entangled state of
n
qubits, the reduced state has two constant eigenvalues \(\frac{1}{2}\). For constant distributions, all Renyi entropies are equivalent:wires = 4 dev = qml.device("default.qubit", wires=range(wires), shots=1000) @qml.qnode(dev) def max_entangled_circuit(): qml.Hadamard(wires=0) for i in range(1, wires): qml.CNOT(wires=[0, i]) return qml.classical_shadow(wires=range(wires)) bits, recipes = max_entangled_circuit() shadow = qml.ClassicalShadow(bits, recipes) entropies = [shadow.entropy(wires=[0], alpha=alpha) for alpha in [1., 2., 3.]]
>>> np.isclose(entropies, entropies[0], atol=1e-2) [ True, True, True]
For non-uniform reduced states that is not the case anymore and the entropy differs for each order
alpha
:@qml.qnode(dev) def qnode(x): for i in range(wires): qml.RY(x[i], wires=i) for i in range(wires - 1): qml.CNOT((i, i + 1)) return qml.classical_shadow(wires=range(wires)) x = np.linspace(0.5, 1.5, num=wires) bitstrings, recipes = qnode(x) shadow = qml.ClassicalShadow(bitstrings, recipes)
>>> [shadow.entropy(wires=wires, alpha=alpha) for alpha in [1., 2., 3.]] [1.5419292874423107, 1.1537924276625828, 0.9593638767763727]
- expval(H, k=1)[source]¶
Compute expectation value of an observable \(H\).
The canonical way of computing expectation values is to simply average the expectation values for each local snapshot, \(\langle O \rangle = \sum_t \text{tr}(\rho^{(t)}O) / T\). This corresponds to the case
k=1
. In the original work, 2002.08953, it has been proposed to split theT
measurements intok
equal parts to compute the median of means. For the case of Pauli measurements and Pauli observables, there is no advantage expected from settingk>1
.One of the main perks of classical shadows is being able to compute many different expectation values by classically post-processing the same measurements. This is helpful in general as it may help save quantum circuit executions.
- Parameters
H (qml.Observable) – Observable to compute the expectation value
k (int) – Number of equal parts to split the shadow’s measurements to compute the median of means.
k=1
(default) corresponds to simply taking the mean over all measurements.
- Returns
expectation value estimate.
- Return type
float
Example
dev = qml.device("default.qubit", wires=range(2), shots=1000) @qml.qnode(dev) def qnode(x): qml.Hadamard(0) qml.CNOT((0,1)) qml.RX(x, wires=0) return qml.classical_shadow(wires=range(2)) bits, recipes = qnode(0) shadow = qml.ClassicalShadow(bits, recipes)
Compute Pauli string observables
>>> shadow.expval(qml.X(0) @ qml.X(1), k=1) array(1.116)
or of a Hamiltonian using the same measurement results
>>> H = qml.Hamiltonian([1., 1.], [qml.Z(0) @ qml.Z(1), qml.X(0) @ qml.X(1)]) >>> shadow.expval(H, k=1) array(1.9980000000000002)
- global_snapshots(wires=None, snapshots=None)[source]¶
Compute the T x 2**n x 2**n global snapshots
Warning
Classical shadows are not intended to reconstruct global quantum states. This method requires exponential scaling of measurements for accurate representations. Further, the output scales exponentially in the output dimension, and is therefore not practical for larger systems. A warning is raised for systems of sizes
n>16
.- Parameters
wires (Iterable[int]) – The wires over which to compute the snapshots. For
wires=None
(default) alln
qubits are used.snapshots (Iterable[int] or int) – Only compute a subset of local snapshots. For
snapshots=None
(default), all local snapshots are taken. In case of an integer, a random subset of that size is taken. The subset can also be explicitly fixed by passing an Iterable with the corresponding indices.
- Returns
The global snapshots tensor of shape
(T, 2**n, 2**n)
containing the density matrices for each snapshot measurement.- Return type
Example
We can approximately reconstruct a Bell state:
dev = qml.device("default.qubit", wires=range(2), shots=1000) @qml.qnode(dev) def qnode(): qml.Hadamard(0) qml.CNOT((0,1)) return classical_shadow(wires=range(2)) bits, recipes = qnode() shadow = ClassicalShadow(bits, recipes) shadow_state = np.mean(shadow.global_snapshots(), axis=0) bell_state = np.array([[0.5, 0, 0, 0.5], [0, 0, 0, 0], [0, 0, 0, 0], [0.5, 0, 0, 0.5]])
>>> np.allclose(bell_state, shadow_state, atol=1e-1) True
- local_snapshots(wires=None, snapshots=None)[source]¶
Compute the T x n x 2 x 2 local snapshots
For each qubit and each snapshot, compute \(3 U_i^\dagger |b_i \rangle \langle b_i| U_i - 1\)
- Parameters
wires (Iterable[int]) – The wires over which to compute the snapshots. For
wires=None
(default) alln
qubits are used.snapshots (Iterable[int] or int) – Only compute a subset of local snapshots. For
snapshots=None
(default), all local snapshots are taken. In case of an integer, a random subset of that size is taken. The subset can also be explicitly fixed by passing an Iterable with the corresponding indices.
- Returns
The local snapshots tensor of shape
(T, n, 2, 2)
containing the local local density matrices for each snapshot and each qubit.- Return type