qml.transforms.split_to_single_terms¶
-
split_to_single_terms
(tape)[source]¶ Splits any expectation values of multi-term observables in a circuit into single term expectation values for devices that don’t natively support measuring expectation values of sums of observables.
- Parameters
tape (QNode or QuantumScript or Callable) – The quantum circuit to modify the measurements of.
- Returns
The transformed circuit as described in
qml.transform
.- Return type
qnode (QNode) or tuple[List[QuantumScript], function]
Note
This transform doesn’t split non-commuting terms into multiple executions. It is suitable for state-based simulators that don’t natively support sums of observables, but can handle non-commuting measurements. For hardware or hardware-like simulators based on projective measurements,
split_non_commuting
should be used instead.Examples:
This transform allows us to transform a QNode that measures multi-term observables into individual measurements, each corresponding to a single term.
dev = qml.device("default.qubit", wires=2) @qml.transforms.split_to_single_terms @qml.qnode(dev) def circuit(x): qml.RY(x[0], wires=0) qml.RX(x[1], wires=1) return [qml.expval(qml.X(0) @ qml.Z(1) + 0.5 * qml.Y(1) + qml.Z(0)), qml.expval(qml.X(1) + qml.Y(1))]
Instead of decorating the QNode, we can also create a new function that yields the same result in the following way:
@qml.qnode(dev) def circuit(x): qml.RY(x[0], wires=0) qml.RX(x[1], wires=1) return [qml.expval(qml.X(0) @ qml.Z(1) + 0.5 * qml.Y(1) + qml.Z(0)), qml.expval(qml.X(1) + qml.Y(1))] circuit = qml.transforms.split_to_single_terms(circuit)
Internally, the QNode measures the individual measurements
>>> print(qml.draw(circuit)([np.pi/4, np.pi/4])) 0: ──RY(0.79)─┤ ╭<X@Z> <Z> 1: ──RX(0.79)─┤ ╰<X@Z> <Y> <X>
Note that the observable
Y(1)
occurs twice in the original QNode, but only once in the transformed circuits. When there are multiple expectation value measurements that rely on the same observable, the observable is measured only once, and the result is copied to each original measurement.While the execution is split into single terms internally, the final result has the same ordering as the user provides in the return statement.
>>> circuit([np.pi/4, np.pi/4]) [0.8535533905932737, -0.7071067811865475]
Usage Details
Internally, this function works with tapes. We can create a tape that returns expectation values of multi-term observables:
measurements = [ qml.expval(qml.Z(0) + qml.Z(1)), qml.expval(qml.X(0) + 0.2 * qml.X(1) + 2 * qml.Identity()), qml.expval(qml.X(1) + qml.Z(1)), ] tape = qml.tape.QuantumScript(measurements=measurements) tapes, processing_fn = qml.transforms.split_to_single_terms(tape)
Now
tapes
is a tuple containing a single tape with the updated measurements, which are now the single-term observables that the original sum observables are composed of:>>> tapes[0].measurements [expval(Z(0)), expval(Z(1)), expval(X(0)), expval(X(1))]
The processing function becomes important as the order of the inputs has been modified. Instead of evaluating the observables in the returned expectation values directly, the four single-term observables are measured, resulting in 4 return values for the execution:
>>> dev = qml.device("default.qubit", wires=2) >>> results = dev.execute(tapes) >>> results ((1.0, 1.0, 0.0, 0.0),)
The processing function can be used to reorganize the results to get the 3 expectation values returned by the circuit:
>>> processing_fn(results) (2.0, 2.0, 1.0)