catalyst.passes.merge_ppr_ppm

merge_ppr_ppm(qnode=None, *, max_pauli_size=0)[source]

A quantum compilation pass that absorbs Clifford Pauli product rotation (PPR) operations, \(\exp{-iP\tfrac{\pi}{4}}\), into the final Pauli product measurements (PPMs).

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:
  • fn (QNode) – QNode to apply the pass to

  • max_pauli_size (int) – The maximum size of Pauli strings resulting from merging. If a merge results in a PPM that acts on more than max_pauli_size qubits, that merge will not be performed. The default value is 0 (no limit).

Returns:

QNode

Example

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

import pennylane as qml
from functools import partial
import jax.numpy as jnp

qml.capture.enable()

@qml.qjit(target="mlir")
@partial(qml.transforms.merge_ppr_ppm, max_pauli_size=2)
@qml.qnode(qml.device("null.qubit", wires=2))
def circuit():
    qml.PauliRot(jnp.pi / 2, pauli_word="Z", wires=0)
    qml.PauliRot(jnp.pi / 2, pauli_word="X", wires=0)
    qml.PauliRot(jnp.pi / 2, pauli_word="Z", wires=0)

    ppm = qml.pauli_measure(pauli_word="ZX", wires=[0, 1])

    return

In the above example, every PPR (PauliRot) and the PPM (pauli_measure) can be merged into one PPM that acts on two qubits. 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 ppr_to_ppm from the PennyLane frontend (qml.transforms.merge_ppr_ppm) instead of with catalyst.passes.merge_ppr_ppm.

>>> print(qml.specs(circuit, level="all")()['resources'])
{
    'No transforms': ...,
    'Before MLIR Passes (MLIR-0)': ...,
    'merge-ppr-ppm (MLIR-1)': Resources(
        num_wires=2,
        num_gates=1,
        gate_types=defaultdict(<class 'int'>, {'PPM-w2': 1}),
        gate_sizes=defaultdict(<class 'int'>, {2: 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.

If a merging resulted in a PPM acting on more than max_pauli_size qubits, that merging operation would be skipped.