catalyst.passes.ppm_compilation

ppm_compilation(qnode=None, *, decompose_method='pauli-corrected', avoid_y_measure=False, max_pauli_size=0)[source]

A quantum compilation pass that transforms Clifford+T gates into Pauli product measurements (PPMs).

This pass combines multiple sub-passes:

The avoid_y_measure and decompose_method arguments are passed to the ppr_to_ppm() pass. The max_pauli_size argument is passed to the commute_ppr() and merge_ppr_ppm() passes.

For more information on PPRs and PPMs, check out the Compilation Hub.

Note

The circuits that generated from this pass are currently not executable on any backend. This pass is only for analysis with the null.qubit device and potential future execution when a suitable backend is available.

Parameters:
  • qnode (QNode, optional) – QNode to apply the pass to. If None, returns a decorator.

  • decompose_method (str, optional) – The method to use for decomposing non-Clifford PPRs. Options are "pauli-corrected", "auto-corrected", and "clifford-corrected". Defaults to "pauli-corrected". "pauli-corrected" uses a reactive measurement for correction that is based on Figure 13 in arXiv:2211.15465. "auto-corrected" uses an additional measurement for correction that is based on Figure 7 in A Game of Surface Codes, and "clifford-corrected" uses a Clifford rotation for correction that is based on Figure 17(b) in A Game of Surface Codes.

  • avoid_y_measure (bool) – Rather than performing a Pauli-Y measurement for Clifford rotations (sometimes more costly), a \(Y\) state (\(Y\vert 0 \rangle\)) is used instead (requires \(Y\)-state preparation). This is currently only supported when using the "clifford-corrected" and "pauli-corrected" decomposition method. Defaults to False.

  • max_pauli_size (int) – The maximum size of the Pauli strings after commuting or merging. Defaults to 0 (no limit).

Returns:

QNode

Example

The commute_ppr compilation pass can be applied as a dectorator on a QNode:

import pennylane as qml
from functools import partial

qml.capture.enable()

@qml.qjit(target="mlir")
@partial(qml.transforms.ppm_compilation, decompose_method="clifford-corrected", max_pauli_size=2)
@qml.qnode(qml.device("null.qubit", wires=2))
def circuit():
    qml.H(0)
    qml.CNOT([0, 1])
    qml.T(0)
    return

For clear and inspectable results, use target="mlir" in the qjit decorator, ensure that PennyLane’s program capture is enabled, pennylane.capture.enable(), and call ppm_compilation from the PennyLane frontend (qml.transforms.ppm_compilation) instead of with catalyst.passes.ppm_compilation.

>>> print(qml.specs(circuit, level="all")()['resources'])
{
    'No transforms': ...,
    'Before MLIR Passes (MLIR-0)': ...,
    'ppm-compilation (MLIR-1)': Resources(
        num_wires=7,
        num_gates=18,
        gate_types=defaultdict(<class 'int'>, {'PPM-w2': 5, 'PPM-w1': 6, 'PPR-pi/2-w1': 5, 'PPM-w3': 1, 'PPR-pi/2-w2': 1}),
        gate_sizes=defaultdict(<class 'int'>, {2: 6, 1: 11, 3: 1}),
        depth=None,
        shots=Shots(total_shots=None, shot_vector=())
    )
}

In the above output, PPM-weight denotes the type of PPM present in the circuit, where weight is the PPM weight. PPR-theta-weight denotes the type of PPR present in the circuit, where theta is the PPR angle (\(\theta\)) and weight is the PPR weight. Note that \(\theta = \tfrac{\pi}{2}\) PPRs correspond to Pauli operators: \(P(\tfrac{\pi}{2}) = \exp(-iP\tfrac{\pi}{2}) = P\). Pauli operators can be commuted to the end of the circuit and absorbed into terminal measurements.

Note that if a commutation or merge resulted in a PPR or PPN acting on more than max_pauli_size qubits (here, max_pauli_size = 2), that commutation or merge would be skipped.