qml.transforms¶
This subpackage contains PennyLane transforms and their building blocks.
Custom transforms¶
qml.transform can be used to define custom transformations that work with PennyLane QNodes and quantum
functions; such transformations can map a circuit to one or many new circuits alongside associated classical post-processing.
|
Generalizes a function that transforms tapes to work with additional circuit-like objects such as a |
Compile Pipeline¶
Multiple transforms can be chained together into a compile pipeline. See Composability of transforms for more details.
|
A sequence of transforms to be applied to a quantum function or a |
Transforms library¶
A collection of ready-to-use transforms are available in PennyLane.
Transforms for circuit compilation¶
A set of transforms to perform basic circuit compilation tasks.
|
Compile a circuit by applying a series of transforms to a quantum function. |
|
Quantum function transform to remove any operations that are applied next to their (self-)inverses or adjoint. |
|
Combine all |
|
Quantum transform to move commuting gates past control and target qubits of controlled operations. |
|
Decomposes a quantum circuit into a user-specified gate set. |
Quantum function transform to combine amplitude embedding templates that act on different qubits. |
|
|
Quantum transform to combine rotation gates of the same type that act sequentially. |
|
Quantum function transform to optimize a circuit given a list of patterns (templates). |
|
Quantum transform to remove Barrier gates. |
Replace 4-qubit relative phase Toffoli gates with their equivalent circuit. |
|
|
Quantum transform to replace controlled iX gates with their equivalent circuit. |
|
Quantum function transform to fuse together groups of single-qubit operations into a general single-qubit unitary operation ( |
|
Transpile a circuit according to a desired coupling map |
|
Quantum function transform to remove SWAP gates by running from right to left through the circuit changing the position of the qubits accordingly. |
|
Quantum function transform to decomposes all instances of single-qubit and select instances of two-qubit |
|
Quantum function transform to decompose all instances of |
Compilation transforms using ZX calculus¶
There is a set of transforms that use ZX calculus to optimize circuits.
|
Reduce the number of T gates in a Clifford + T circuit by using basic commutation and cancellation rules, combined with a dedicated phase-polynomial optimization strategy based on the Third Order Duplicate and Destroy (TODD) algorithm. |
|
Push Hadamard gates as far as possible to one side to cancel them and create fewer larger phase-polynomial blocks, improving the effectiveness of phase-polynomial optimization techniques. |
|
Reduce the number of non-Clifford gates by applying a combination of phase gadgetization strategies and Clifford gate simplification rules. |
|
Apply the Third Order Duplicate and Destroy (TODD) algorithm to reduce the number of T gates in a given Clifford + T circuit. |
The following utility functions assist when working explicitly with ZX diagrams, for example when writing custom ZX compilation passes. Also see the section on intermediate representations below.
|
This transform converts a PennyLane quantum tape to a ZX-Graph in the PyZX framework. |
|
Converts a graph from PyZX to a PennyLane tape, if the graph is diagram-like. |
Other compilation utilities¶
There are additional utility functions and decompositions available that assist with both transforms and decompositions within the larger PennyLane codebase.
|
Context manager for setting custom decompositions. |
|
Function that applies the pattern matching algorithm and returns the list of maximal matches. |
There are also utility functions that take a circuit and return a DAG.
|
Construct the pairwise-commutation DAG (directed acyclic graph) representation of a quantum circuit. |
|
Class to represent a quantum circuit as a directed acyclic graph (DAG). |
|
Class to store information about a quantum operation in a node of the commutation DAG. |
Transforms for Clifford+T decomposition and Pauli-based computation¶
These transforms accept quantum circuits and decomposes them to the Clifford+T basis.
|
Decomposes a circuit into the Clifford+T basis. |
|
Decomposes RZ and PhaseShift gates into the Clifford+T basis or the PPR basis. |
|
A quantum compilation pass that transforms Clifford+T gates into Pauli product measurements (PPMs). |
|
A quantum compilation pass that converts Clifford+T gates into Pauli Product Rotation (PPR) gates. |
|
A quantum compilation pass that commutes Clifford Pauli product rotation (PPR) gates, \(\exp(-{iP\tfrac{\pi}{4}})\), past non-Clifford PPRs gates, \(\exp(-{iP\tfrac{\pi}{8}})\), where \(P\) is a Pauli word. |
|
A quantum compilation pass that decomposes Pauli product rotations (PPRs), \(P(\theta) = \exp(-iP\theta)\), into Pauli product measurements (PPMs). |
|
A quantum compilation pass that absorbs Clifford Pauli product rotation (PPR) operations, \(\exp{-iP\tfrac{\pi}{4}}\), into the final Pauli product measurements (PPMs). |
|
A quantum compilation pass that reduces the depth and count of non-Clifford Pauli product rotation (PPR, \(P(\theta) = \exp(-iP\theta)\)) operators (e.g., |
|
A quantum compilation pass that decomposes arbitrary-angle Pauli product rotations (PPRs) into a collection of PPRs (with angles of rotation of \(\tfrac{\pi}{2}\), \(\tfrac{\pi}{4}\), and \(\tfrac{\pi}{8}\)), PPMs and a single-qubit arbitrary-angle PPR in the Z basis. |
Other transforms¶
These are additional transforms that are useful for multiple purposes such as circuit preprocessing, getting information from a circuit, and more.
|
Transform a QNode to support an initial batch dimension for operation parameters. |
|
Transform a circuit to support an initial batch dimension for gate inputs. |
|
Expand a broadcasted tape into multiple tapes and a function that stacks and squeezes the results. |
|
Splits a tape measuring a (fast-forwardable) Hamiltonian expectation into mutliple tapes of the Xi or sgn decomposition, and provides a function to recombine the results. |
Transforms a circuit to one with purely numpy parameters. |
|
|
Quantum function transform that substitutes operations conditioned on measurement outcomes to controlled operations. |
|
Diagonalize a set of measurements into the standard basis. |
|
Splits a circuit into tapes measuring groups of commuting observables. |
|
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. |
|
Applies the transform that performs a controlled version of the \(\mathcal{Q}\) unitary defined in this paper. |
|
Applies the transform quantum Monte Carlo estimation algorithm. |
|
Map dynamic wires to concrete values determined by the provided |
Transforms for intermediate representations¶
Intermediate representations (IRs) are alternative representations of quantum circuits, typically offering a more efficient classical description for special classes of circuits. The following functions produce intermediate representations of quantum circuits, or use them internally to produce a new quantum circuit:
|
Compute the parity matrix intermediate representation of a CNOT circuit. |
|
Pass for applying ParitySynth to phase polynomials in a circuit. |
|
Compute the Phase polynomial intermediate representation for circuits consisting of |
|
CNOT routing algorithm RowCol. |
In addition, there are the following utility functions to traverse a graph:
|
Post-order traverse a tree graph, starting from (but excluding) the node |
|
Pre-order traverse a tree graph, starting from (but excluding) the node |
Transforms that act only on QNodes¶
These transforms only accept QNodes, and return new transformed functions that compute the desired quantity.
|
Create a batched partial callable object from the QNode specified. |
|
Create a function that draws the given QNode or quantum function. |
|
Draw a qnode with matplotlib |
Transforms developer classes¶
|
A transform with bound inputs. |
|
Generalizes a function that transforms tapes to work with additional circuit-like objects such as a |
Transforming circuits¶
A quantum transform is a crucial concept in PennyLane, and refers to mapping a quantum circuit to one or more circuits, alongside a classical post-processing function. Once a transform is registered with PennyLane, the transformed circuits will be executed, and the classical post-processing function automatically applied to the outputs. This becomes particularly valuable when a transform generates multiple circuits, requiring a method to aggregate or reduce the results (e.g., applying the parameter-shift rule or computing the expectation value of a Hamiltonian term-by-term).
Note
For examples of built-in transforms that come with PennyLane, see the Compiling circuits documentation.
Transforms can be applied on QNodes using the decorator syntax:
dev = qml.device("default.qubit", wires=2)
@qml.transforms.split_non_commuting
@qml.qnode(dev)
def circuit(params):
qml.RX(params[0], wires=0)
qml.RZ(params[1], wires=1)
return [
qml.expval(qml.X(0)),
qml.expval(qml.Y(1)),
qml.expval(qml.Z(0) @ qml.Z(1)),
qml.expval(qml.X(0) @ qml.Z(1) + 0.5 * qml.Y(1) + qml.Z(0)),
]
Passing arguments to transforms¶
We can pass additional arguments to a transform that accepts them, which binds them with
the transform, creating a BoundTransform, which can
then be applied on a QNode. In the following example, we pass the keyword argument
grouping_strategy="wires" to the split_non_commuting() transform, which splits
a circuit into tapes measuring groups of commuting observables.
dev = qml.device("default.qubit", wires=2)
@qml.transforms.split_non_commuting(grouping_strategy="wires")
@qml.qnode(dev)
def circuit(params):
qml.RX(params[0], wires=0)
qml.RZ(params[1], wires=1)
return [
qml.expval(qml.X(0)),
qml.expval(qml.Y(1)),
qml.expval(qml.Z(0) @ qml.Z(1)),
qml.expval(qml.X(0) @ qml.Z(1) + 0.5 * qml.Y(1) + qml.Z(0)),
]
Composability of transforms¶
Transforms are inherently composable on a QNode, meaning that transforms
with compatible post-processing functions can be successively applied to QNodes. For
example, this allows for the application of multiple compilation passes on a QNode
to maximize gate reduction before execution.
dev = qml.device("default.qubit", wires=1)
@qml.transforms.merge_rotations
@qml.transforms.cancel_inverses(recursive=True)
@qml.qnode(device=dev)
def circuit(x, y):
qml.X(wires=0)
qml.Hadamard(wires=0)
qml.Hadamard(wires=0)
qml.X(wires=0)
qml.RX(x, wires=0)
qml.RX(y, wires=0)
return qml.expval(qml.Z(0))
In this example, cancel_inverses is applied first, which will remove the two Hadamard
gates and the two Pauli X gates. Subsequently, merge_rotations will be applied, which
will merge the two RX rotations into a single RX gate.
Alternatively, multiple transforms can be chained together to create a CompilePipeline:
>>> pipeline = qml.transforms.cancel_inverses(recursive=True) + qml.transforms.merge_rotations
>>> pipeline
CompilePipeline(cancel_inverses, merge_rotations)
The CompilePipeline can also be applied on a QNode, which will transform the
circuit with each pass within the pipeline sequentially.
@pipeline
@qml.qnode(device=dev)
def circuit(x, y):
qml.X(wires=0)
qml.Hadamard(wires=0)
qml.Hadamard(wires=0)
qml.X(wires=0)
qml.RX(x, wires=0)
qml.RX(y, wires=0)
return qml.expval(qml.Z(0))
Creating your own transform¶
To streamline the creation of transforms and ensure their versatility across various
circuit abstractions in PennyLane, the pennylane.transform() is available.
This decorator registers transforms that accept a QuantumScript as its
primary input and returns a sequence of QuantumScript and an associated
processing function.
To illustrate the process of creating a quantum transform, let’s consider an example.
Suppose we want a transform that removes all RX operations from a given
circuit. In this case, we merely need to filter the original QuantumScript
and return a new one without the filtered operations. As we don’t require a specific
processing function in this scenario, we include a function that simply returns the
first and only result.
from pennylane.tape import QuantumScript, QuantumScriptBatch
from pennylane.typing import PostprocessingFn
@qml.transform
def remove_rx(tape: QuantumScript) -> tuple[QuantumScriptBatch, PostprocessingFn]:
operations = filter(lambda op: op.name != "RX", tape.operations)
new_tape = tape.copy(operations=operations)
def null_postprocessing(results):
return results[0]
return [new_tape], null_postprocessing
The @qml.transform decorator makes it applicable to a QNode:
@remove_rx
@qml.qnode(qml.device("default.qubit"))
def circuit():
qml.RX(0.5, wires=0)
qml.RY(0.5, wires=1)
qml.CNOT([0, 1])
return qml.expval(qml.Z(0))
For a more advanced example, let’s consider a transform that sums a circuit with its
adjoint. We define the adjoint of the tape operations, create a new tape with these
new operations, and return both tapes. The processing function then sums the results
of the original and the adjoint tape. In this example, we use qml.transform in
the form of a decorator in order to turn the custom function into a quantum transform.
from pennylane.tape import QuantumScript, QuantumScriptBatch
from pennylane.typing import PostprocessingFn
@qml.transform
def sum_circuit_and_adjoint(tape: QuantumScript) -> tuple[QuantumScriptBatch, PostprocessingFn]:
operations = [qml.adjoint(op) for op in tape.operation]
new_tape = tape.copy(operations=operations)
def sum_postprocessing(results):
return qml.sum(results)
return [tape, new_tape], sum_postprocessing
Additional information¶
Explore practical examples of transforms focused on compiling circuits in the compiling circuits documentation. For gradient transforms, refer to the examples in the gradients documentation. Finally, for a comprehensive overview of transforms and core functionalities, consult the summary above.