qml.snapshots¶
- snapshots(tape)[source]¶
This transform processes
Snapshot
instances contained in a circuit, depending on the compatibility of the execution device. For supported devices, the snapshots’ measurements are computed as the execution progresses. Otherwise, theQuantumTape
gets split into several, one for each snapshot, with each aggregating all the operations up to that specific snapshot.- Parameters
tape (QNode or QuantumTape or Callable) – a quantum circuit.
- Returns
The transformed circuit as described in
qml.transform
.- Return type
dictionary (dict) or qnode (QNode) or quantum function (Callable) or tuple[List[QuantumTape], function]
If tape splitting is carried out, the transform will be conservative about the wires that it includes in each tape. So, if all operations preceding a snapshot in a 3-qubit circuit has been applied to only one wire, the tape would only be looking at this wire. This can be overriden by the configuration of the execution device and its nature.
Regardless of the transform’s behaviour, the output is a dictionary where each key is either the tag supplied to the snapshot or its index in order of appearance, in addition to an
"execution_results"
entry that returns the final output of the quantum circuit. The post-processing function is responsible for aggregating the results into this dictionary.When the transform is applied to a QNode, the
shots
configuration is inherited from the device. Therefore, the snapshot measurements must be supported by the device’s nature. An exception of this are theqml.state
measurements on finite-shot simulators.Warning
For devices that do not support snapshots (e.g QPUs, external plug-in simulators), be mindful of additional costs that you might incur due to the 1 separate execution/snapshot behaviour.
Example
dev = qml.device("default.qubit", wires=2) @qml.qnode(dev, interface=None) def circuit(): qml.Snapshot(measurement=qml.expval(qml.Z(0))) qml.Hadamard(wires=0) qml.CNOT(wires=[0, 1]) qml.Snapshot() return qml.expval(qml.X(0))
>>> qml.snapshots(circuit)() {0: 1.0, 1: array([0.70710678+0.j, 0. +0.j, 0. +0.j, 0.70710678+0.j]), 'execution_results': 0.0}
dev = qml.device("default.qubit", shots=200, wires=2) @qml.snapshots @qml.qnode(dev, interface=None) def circuit(): qml.Hadamard(wires=0) qml.Snapshot() qml.CNOT(wires=[0, 1]) return qml.counts()
>>> circuit() {0: array([0.70710678+0.j, 0. +0.j, 0.70710678+0.j, 0. +0.j]), 'execution_results': {'00': 101, '11': 99}}
Here one can see how a device that does not natively support snapshots executes two different circuits. Additionally, a warning is raised along with the results:
dev = qml.device("lightning.qubit", shots=100, wires=2) @qml.snapshots @qml.qnode(dev) def circuit(): qml.Hadamard(wires=0), qml.Snapshot(qml.counts()) qml.CNOT(wires=[0, 1]) return qml.expval(qml.PauliZ(0)) with circuit.device.tracker: out = circuit()
>>> circuit.device.tracker.totals UserWarning: Snapshots are not supported for the given device. Therefore, a tape will be created for each snapshot, resulting in a total of n_snapshots + 1 executions. warnings.warn( {'batches': 1, 'simulations': 2, 'executions': 2, 'shots': 200, 'results': -0.16}
>>> out {0: {'00': tensor(52, requires_grad=True), '10': tensor(48, requires_grad=True)}, 'execution_results': tensor(-0.1, requires_grad=True)}
Here you can see the default behaviour of the transform for unsupported devices and you can see how the amount of wires included in each resulting tape is minimal:
ops = [ qml.Snapshot(), qml.Hadamard(wires=0), qml.Snapshot("very_important_state"), qml.CNOT(wires=[0, 1]), qml.Snapshot(), ] measurements = [qml.expval(qml.PauliX(0))] tape = qml.tape.QuantumTape(ops, measurements) tapes, collect_results_into_dict = qml.snapshots(tape)
>>> print(tapes) [<QuantumTape: wires=[], params=0>, <QuantumTape: wires=[0], params=0>, <QuantumTape: wires=[0, 1], params=0>, <QuantumTape: wires=[0, 1], params=0>]