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
T
is the number of shots andn
is 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_shadow
measurement 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:
dev = qml.device("default.qubit", wires=2, 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
QuantumTape
interface should be used instead:dev = qml.device("default.qubit", wires=2, shots=5) 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, gradient_fn=None)[0] >>> bits2, recipes2 = qml.execute([tape], device=dev, gradient_fn=None)[0] >>> np.all(recipes1 == recipes2) True >>> np.all(bits1 == bits2) False
If using different Pauli recipes is desired for the
QuantumTape
interface, different seeds should be used for the classical shadow:dev = qml.device("default.qubit", wires=2, shots=5) 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, gradient_fn=None)[0] >>> bits2, recipes2 = qml.execute([tape2], device=dev, gradient_fn=None)[0] >>> np.all(recipes1 == recipes2) False >>> np.all(bits1 == bits2) False