qml.transforms.core.transform

transform(quantum_transform, expand_transform=None, classical_cotransform=None, is_informative=False, final_transform=False, use_argnum_in_expand=False)[source]

Generalizes a function that transforms tapes to work with additional circuit-like objects such as a QNode.

transform should be applied to a function that transforms tapes. Once validated, the result will be an object that is able to transform PennyLane’s range of circuit-like objects: QuantumTape, quantum function and QNode. A circuit-like object can be transformed either via decoration or by passing it functionally through the created transform.

Parameters

quantum_transform (Callable) –

The input quantum transform must be a function that satisfies the following requirements:

  • Accepts a QuantumTape as its first input and returns a sequence of QuantumTape and a processing function.

  • The transform must have the following structure (type hinting is optional): my_quantum_transform(tape: qml.tape.QuantumScript, ...) -> tuple[qml.tape.QuantumScriptBatch, qml.typing.PostprocessingFn]

Keyword Arguments
  • expand_transform=None (Optional[Callable]) – An optional expand transform is applied directly before the input quantum transform. It must be a function that satisfies the same requirements as quantum_transform.

  • classical_cotransform=None (Optional[Callable]) – A classical co-transform is a function to post-process the classical jacobian and the quantum jacobian and has the signature: my_cotransform(qjac, cjac, tape) -> tensor_like

  • is_informative=False (bool) – Whether or not a transform is informative. If true the transform is queued at the end of the transform program and the tapes or qnode aren’t executed.

  • final_transform=False (bool) – Whether or not the transform is terminal. If true the transform is queued at the end of the transform program. is_informative supersedes final_transform.

  • use_argnum_in_expand=False (bool) – Whether or not to use argnum of the tape to determine trainable parameters during the expansion transform process.

Returns

Returns a transform dispatcher object that that can transform any circuit-like object in PennyLane.

Return type

TransformDispatcher

Example

First define an input quantum transform with the necessary structure defined above. In this example we copy the tape and sum the results of the execution of the two tapes.

from pennylane.tape import QuantumScript, QuantumScriptBatch
from pennylane.typing import PostprocessingFn

def my_quantum_transform(tape: QuantumScript) -> tuple[QuantumScriptBatch, PostprocessingFn]:
    tape1 = tape
    tape2 = tape.copy()

    def post_processing_fn(results):
        return qml.math.sum(results)

    return [tape1, tape2], post_processing_fn

We want to be able to apply this transform on both a qfunc and a pennylane.QNode and will use transform to achieve this. transform validates the signature of your input quantum transform and makes it capable of transforming qfunc and pennylane.QNode in addition to quantum tapes. Let’s define a circuit as a pennylane.QNode:

dev = qml.device("default.qubit")

@qml.qnode(device=dev)
def qnode_circuit(a):
    qml.Hadamard(wires=0)
    qml.CNOT(wires=[0, 1])
    qml.X(0)
    qml.RZ(a, wires=1)
    return qml.expval(qml.Z(0))

We first apply transform to my_quantum_transform:

>>> dispatched_transform = transform(my_quantum_transform)

Now you can use the dispatched transform directly on a pennylane.QNode.

For pennylane.QNode, the dispatched transform populates the TransformProgram of your QNode. The transform and its processing function are applied in the execution.

>>> transformed_qnode = dispatched_transform(qnode_circuit)
<QNode: wires=2, device='default.qubit', interface='auto', diff_method='best'>
>>> transformed_qnode.transform_program
TransformProgram(my_quantum_transform)

If we apply dispatched_transform a second time to the pennylane.QNode, we would add it to the transform program again and therefore the transform would be applied twice before execution.

>>> transformed_qnode = dispatched_transform(transformed_qnode)
>>> transformed_qnode.transform_program
TransformProgram(my_quantum_transform, my_quantum_transform)

When a transformed QNode is executed, the QNode’s transform program is applied to the generated tape and creates a sequence of tapes to be executed. The execution results are then post-processed in the reverse order of the transform program to obtain the final results.

We can compose multiple transforms when working in the tape paradigm and apply them to more than one tape. The following example demonstrates how to apply a transform to a batch of tapes.

Example

In this example, we apply sequentially a transform to a tape and another one to a batch of tapes. We then execute the transformed tapes on a device and post-process the results.

import pennylane as qml

H = qml.PauliY(2) @ qml.PauliZ(1) + 0.5 * qml.PauliZ(2) + qml.PauliZ(1)
measurement = [qml.expval(H)]
operations = [qml.Hadamard(0), qml.RX(0.2, 0), qml.RX(0.6, 0), qml.CNOT((0, 1))]
tape = qml.tape.QuantumTape(operations, measurement)

batch1, function1 = qml.transforms.split_non_commuting(tape)
batch2, function2 = qml.transforms.merge_rotations(batch1)

dev = qml.device("default.qubit", wires=3)
result = dev.execute(batch2)

The first split_non_commuting transform splits the original tape, returning a batch of tapes batch1 and a processing function function1. The second merge_rotations transform is applied to the batch of tapes returned by the first transform. It returns a new batch of tapes batch2, each of which has been transformed by the second transform, and a processing function function2.

>>> batch2
(<QuantumTape: wires=[0, 1, 2], params=2>,
<QuantumTape: wires=[0, 1, 2], params=1>)
>>> type(function2)
function

We can combine the processing functions to post-process the results of the execution.

>>> function1(function2(result))
[array(0.5)]

A dispatched transform is able to handle several PennyLane circuit-like objects:

For each object, the transform will be applied in a different way, but it always preserves the underlying tape-based quantum transform behaviour.

The return of a dispatched transform depends upon which of the above objects is passed as an input:

  • For a QNode input, the underlying transform is added to the QNode’s TransformProgram and the return is the transformed QNode. For each execution of the pennylane.QNode, it first applies the transform program on the original captured circuit. Then the transformed circuits are executed by a device and finally the post-processing function is applied on the results.

  • For a quantum function (callable) input, the transform builds the tape when the quantum function is executed and then applies itself to the tape. The resulting tape is then converted back to a quantum function (callable). It therefore returns a transformed quantum function (Callable). The limitation is that the underlying transform can only return a sequence containing a single tape, because quantum functions only support a single circuit.

  • For a QuantumTape, the underlying quantum transform is directly applied on the QuantumTape. It returns a sequence of QuantumTape and a processing function to be applied after execution.

  • For a batch of pennylane.tape.QuantumTape, the quantum transform is mapped across all the tapes. It returns a sequence of QuantumTape and a processing function to be applied after execution. Each tape in the sequence is transformed by the transform.

  • For a Device, the transform is added to the device’s transform program and a transformed pennylane.devices.Device is returned. The transform is added to the end of the device program and will be last in the overall transform program.