qml.classical_shadow¶
- classical_shadow(wires, seed=None)[source]¶
The classical shadow measurement protocol.
The protocol is described in detail in the paper Predicting Many Properties of a Quantum System from Very Few Measurements. This measurement process returns the randomized Pauli measurements (the
recipes) that are performed for each qubit and snapshot as an integer:0 for Pauli X,
1 for Pauli Y, and
2 for Pauli Z.
It also returns the measurement results (the
bits); 0 if the 1 eigenvalue is sampled, and 1 if the -1 eigenvalue is sampled.The device shots are used to specify the number of snapshots. If
Tis the number of shots andnis the number of qubits, then both the measured bits and the Pauli measurements have shape(T, n).- Parameters:
wires (Sequence[int]) – the wires to perform Pauli measurements on
seed (Union[None, int]) – Seed used to randomly sample Pauli measurements during the classical shadows protocol. If None, a random seed will be generated. If a tape with a
classical_shadowmeasurement is copied, the seed will also be copied. Different seeds are still generated for different constructed tapes.
- Returns:
measurement process instance
- Return type:
Example
Consider the following QNode that prepares a Bell state and performs a classical shadow measurement:
from functools import partial dev = qml.device("default.qubit", wires=2) @partial(qml.set_shots, shots=5) @qml.qnode(dev) def circuit(): qml.Hadamard(wires=0) qml.CNOT(wires=[0, 1]) return qml.classical_shadow(wires=[0, 1])
Executing this QNode produces the sampled bits and the Pauli measurements used:
>>> bits, recipes = circuit() >>> bits tensor([[0, 0], [1, 0], [1, 0], [0, 0], [0, 1]], dtype=uint8, requires_grad=True) >>> recipes tensor([[2, 2], [0, 2], [1, 0], [0, 2], [0, 2]], dtype=uint8, requires_grad=True)
Usage Details
Consider again the QNode in the above example. Since the Pauli observables are randomly sampled, executing this QNode again would produce different bits and Pauli recipes:
>>> bits, recipes = circuit() >>> bits tensor([[0, 1], [0, 1], [0, 0], [0, 1], [1, 1]], dtype=uint8, requires_grad=True) >>> recipes tensor([[1, 0], [2, 1], [2, 2], [1, 0], [0, 0]], dtype=uint8, requires_grad=True)
To use the same Pauli recipes for different executions, the
QuantumTapeinterface should be used instead:dev = qml.device("default.qubit", wires=2) ops = [qml.Hadamard(wires=0), qml.CNOT(wires=(0,1))] measurements = [qml.classical_shadow(wires=(0,1))] tape = qml.tape.QuantumTape(ops, measurements, shots=5)
>>> bits1, recipes1 = qml.execute([tape], device=dev, diff_method=None)[0] >>> bits2, recipes2 = qml.execute([tape], device=dev, diff_method=None)[0] >>> np.all(recipes1 == recipes2) True >>> np.all(bits1 == bits2) False
If using different Pauli recipes is desired for the
QuantumTapeinterface, different seeds should be used for the classical shadow:dev = qml.device("default.qubit", wires=2) measurements1 = [qml.classical_shadow(wires=(0,1), seed=10)] tape1 = qml.tape.QuantumTape(ops, measurements1, shots=5) measurements2 = [qml.classical_shadow(wires=(0,1), seed=15)] tape2 = qml.tape.QuantumTape(ops, measurements2, shots=5)
>>> bits1, recipes1 = qml.execute([tape1], device=dev, diff_method=None)[0] >>> bits2, recipes2 = qml.execute([tape2], device=dev, diff_method=None)[0] >>> np.all(recipes1 == recipes2) False >>> np.all(bits1 == bits2) False