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; such transformations can map a circuit to one or many new circuits alongside associated classical post-processing.

transform(quantum_transform[, …])

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

Transforms library

A range of ready-to-use transforms are available in PennyLane.

Transforms for circuit compilation

A set of transforms to perform basic circuit compilation tasks.

compile(tape[, pipeline, basis_set, …])

Compile a circuit by applying a series of transforms to a quantum function.

cancel_inverses(tape)

Quantum function transform to remove any operations that are applied next to their (self-)inverses or adjoint.

commute_controlled(tape[, direction])

Quantum transform to move commuting gates past control and target qubits of controlled operations.

merge_rotations(tape[, atol, include_gates])

Quantum transform to combine rotation gates of the same type that act sequentially.

single_qubit_fusion(tape[, atol, exclude_gates])

Quantum function transform to fuse together groups of single-qubit operations into a general single-qubit unitary operation (Rot).

unitary_to_rot(tape)

Quantum function transform to decomposes all instances of single-qubit and select instances of two-qubit QubitUnitary operations to parametrized single-qubit operations.

merge_amplitude_embedding(tape)

Quantum function transform to combine amplitude embedding templates that act on different qubits.

remove_barrier(tape)

Quantum transform to remove Barrier gates.

undo_swaps(tape)

Quantum function transform to remove SWAP gates by running from right to left through the circuit changing the position of the qubits accordingly.

pattern_matching_optimization(tape, …[, …])

Quantum function transform to optimize a circuit given a list of patterns (templates).

transpile(tape, coupling_map[, device])

Transpile a circuit according to a desired coupling map

There are also utility functions and decompositions available that assist with both transforms, and decompositions within the larger PennyLane codebase.

set_decomposition(custom_decomps, dev[, …])

Context manager for setting custom decompositions.

pattern_matching(circuit_dag, pattern_dag)

Function that applies the pattern matching algorithm and returns the list of maximal matches.

to_zx(tape[, expand_measurements])

This transform converts a PennyLane quantum tape to a ZX-Graph in the PyZX framework.

from_zx(graph[, decompose_phases])

Converts a graph from PyZX to a PennyLane tape, if the graph is diagram-like.

There are also utility functions that take a circuit and return a DAG.

commutation_dag(tape)

Construct the pairwise-commutation DAG (directed acyclic graph) representation of a quantum circuit.

CommutationDAG(tape)

Class to represent a quantum circuit as a directed acyclic graph (DAG).

CommutationDAGNode([op, wires, …])

Class to store information about a quantum operation in a node of the commutation DAG.

Transform for Clifford+T decomposition

This transform accepts quantum circuits and decomposes them to the Clifford+T basis.

clifford_t_decomposition(tape[, epsilon, …])

Decomposes a circuit into the Clifford+T basis.

Transforms for error mitigation

mitigate_with_zne(tape, scale_factors, …)

Mitigate an input circuit using zero-noise extrapolation.

fold_global(tape, scale_factor)

Differentiable circuit folding of the global unitary circuit.

poly_extrapolate(x, y, order)

Extrapolator to \(f(0)\) for polynomial fit.

richardson_extrapolate(x, y)

Polynomial fit where the degree of the polynomial is fixed to being equal to the length of x.

Other transforms

These transforms use the pennylane.transform() function / decorator and can be used on pennylane.tape.QuantumTape, pennylane.QNode. They fulfill multiple purposes like circuit preprocessing, get information from a circuit and more.

batch_params(tape[, all_operations])

Transform a QNode to support an initial batch dimension for operation parameters.

batch_input(tape, argnum)

Transform a circuit to support an initial batch dimension for gate inputs.

insert(tape, op, op_args[, position, before])

Insert an operation into specified points in an input circuit.

defer_measurements(tape, **kwargs)

Quantum function transform that substitutes operations conditioned on measurement outcomes to controlled operations.

split_non_commuting(tape)

Splits a qnode measuring non-commuting observables into groups of commuting observables.

broadcast_expand(tape)

Expand a broadcasted tape into multiple tapes and a function that stacks and squeezes the results.

hamiltonian_expand(tape[, group])

Splits a tape measuring a Hamiltonian expectation into mutliple tapes of Pauli expectations, and provides a function to recombine the results.

sign_expand(tape[, circuit, J, delta, controls])

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.

sum_expand(tape[, group])

Splits a quantum tape measuring a Sum expectation into multiple tapes of summand expectations, and provides a function to recombine the results.

convert_to_numpy_parameters(circuit)

Transforms a circuit to one with purely numpy parameters.

apply_controlled_Q(tape, wires, target_wire, …)

Applies the transform that performs a controlled version of the \(\mathcal{Q}\) unitary defined in this paper.

quantum_monte_carlo(tape, wires, …)

Applies the transform quantum Monte Carlo estimation algorithm.

Transforms that act only on QNodes

These transforms only accept QNodes, and return new transformed functions that compute the desired quantity.

batch_partial(qnode[, all_operations, …])

Create a batched partial callable object from the QNode specified.

draw(qnode[, wire_order, show_all_wires, …])

Create a function that draws the given qnode or quantum function.

draw_mpl(qnode[, wire_order, …])

Draw a qnode with matplotlib

Decorators and utility functions

The following decorators and convenience functions are provided to help build custom QNode, quantum function, and tape transforms:

make_tape(fn)

Returns a function that generates the tape from a quantum function without any operation queuing taking place.

map_batch_transform(transform, tapes)

Map a transform over multiple tapes.

create_expand_fn(depth[, stop_at, device, …])

Create a function for expanding a tape to a given depth, and with a specific stopping criterion.

create_decomp_expand_fn(custom_decomps, dev)

Creates a custom expansion function for a device that applies a set of specified custom decompositions.

expand_invalid_trainable(tape[, depth])

Expand out a tape so that it supports differentiation of requested operations.

expand_invalid_trainable_hadamard_gradient(tape)

Expand out a tape so that it supports differentiation of requested operations with the Hadamard test gradient.

expand_multipar(tape[, depth])

Expand out a tape so that all its parametrized operations have a single parameter.

expand_trainable_multipar(tape[, depth])

Expand out a tape so that all its trainable operations have a single parameter.

expand_nonunitary_gen(tape[, depth])

Expand out a tape so that all its parametrized operations have a unitary generator.

Transforms developer functions

TransformContainer, TransformDispatcher and TransformProgram are developer-facing objects that allow the creation, dispatching and composability of transforms. If you would like to make a custom transform, refer instead to the documentation of qml.transform.

transform_dispatcher

This module contains the transform dispatcher and the transform container.

transform_program

This module contains the TransformProgram class.

Old transforms framework

These utility functions were previously used to create transforms in PennyLane and are now deprecated. It is now recommended to use qml.transform for the creation of custom transforms.

single_tape_transform(transform_fn)

For registering a tape transform that takes a tape and outputs a single new tape.

batch_transform(*args, **kwargs)

Class for registering a tape transform that takes a tape, and outputs a batch of tapes to be independently executed on a quantum device.

qfunc_transform(tape_transform)

Given a function which defines a tape transform, convert the function into one that applies the tape transform to quantum functions (qfuncs).

op_transform(*args, **kwargs)

Convert a function that applies to operators into a functional transform.

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.

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 QuantumTape as its primary input, and returns a sequence of QuantumTape and an associated processing function.

To illustrate the process of creating a quantum transform, let’s consider a straightforward 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 QuantumTape 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 typing import Sequence, Callable
from pennylane.tape import QuantumTape

def remove_rx(tape: QuantumTape) -> (Sequence[QuantumTape], Callable):

    operations = filter(lambda op: op.name != "RX", tape.operations)
    new_tape = type(tape)(operations, tape.measurements, shots=tape.shots)

    def postprocessing(results):
        return results[0]

    return [new_tape], postprocessing

To make your transform applicable to both QNode and quantum functions, you can use the pennylane.transform() decorator.

dispatched_transform = qml.transform(remove_rx)

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, and return both tapes. The processing function then sums the results.

from typing import Sequence, Callable
from pennylane.tape import QuantumTape

@qml.transform
def sum_circuit_and_adjoint(tape: QuantumTape) -> (Sequence[QuantumTape], Callable):

    operations = [qml.adjoint(op) for op in tape.operation]
    new_tape = type(tape)(operations, tape.measurements, shots=tape.shots)

    def null_postprocessing(results):
        return qml.sum(results)

    return [tape, shifted_tape], null_postprocessing

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
@qml.qnode(device=dev):
def circuit(x, y):
    qml.Hadamard(wires=0)
    qml.Hadamard(wires=0)
    qml.RX(x, wires=0)
    qml.RY(y, wires=0)
    qml.RZ(y, wires=0)
    qml.RY(x, wires=0)
    return qml.expval(qml.Z(0))

In this example, inverses are canceled, leading to the removal of two Hadamard gates. Subsequently, rotations are merged into a single qml.Rot gate. Consequently, two transforms are successfully applied to the circuit.

Additional information

Explore practical examples of transforms focused on compiling circuits in the compiling circuits documentation. For gradient transforms, refer to the examples in gradients documentation. Discover quantum information transformations in the quantum information documentation. Finally, for a comprehensive overview of transforms and core functionalities, consult the transforms documentation.