catalyst.passes.ppr_to_ppm

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

A quantum compilation pass that decomposes Pauli product rotations (PPRs), \(P(\theta) = \exp(-iP\theta)\), into Pauli product measurements (PPMs).

Note

This transform requires decorating the workflow with @qml.qjit. In addition, the circuits generated by this pass are currently executable on lightning.qubit or null.qubit (for mock-execution).

Lastly, the pennylane.transforms.to_ppr() transform must be applied before ppr_to_ppm.

This pass is used to decompose both non-Clifford and Clifford PPRs into PPMs. The non-Clifford PPRs (\(\theta = \tfrac{\pi}{8}\)) are decomposed first, then Clifford PPRs (\(\theta = \tfrac{\pi}{4}\)) are decomposed.

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

Parameters:
  • qnode (QNode) – QNode to apply the pass to.

  • decompose_method (str) – 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 methods. Defaults to False.

Returns:

QNode

See also

to_ppr(), commute_ppr(), merge_ppr_ppm(), ppm_compilation(), reduce_t_depth(), decompose_arbitrary_ppr()

Note

For better compatibility with other PennyLane functionality, ensure that PennyLane program capture is enabled with @qjit(capture=True).

Example

The ppr_to_ppm compilation pass can be applied as a decorator on a QNode:

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

@qml.qjit(capture=True)
@qml.transforms.ppr_to_ppm
@qml.transforms.to_ppr
@qml.qnode(qml.device("null.qubit", wires=2))
def circuit():
    # equivalent to a Hadamard gate
    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)

    # equivalent to a CNOT gate
    qml.PauliRot(jnp.pi / 2, pauli_word="ZX", wires=[0, 1])
    qml.PauliRot(-jnp.pi / 2, pauli_word="Z", wires=[0])
    qml.PauliRot(-jnp.pi / 2, pauli_word="X", wires=[1])

    # equivalent to a T gate
    qml.PauliRot(jnp.pi / 4, pauli_word="Z", wires=0)

    return qml.expval(qml.Z(0))
>>> print(qml.specs(circuit, level=2)())
Device: null.qubit
Device wires: 2
Shots: Shots(total=None)
Level: ppr-to-ppm

Wire allocations: 8
Total gates: 22
Gate counts:
- PPM-w2: 6
- PPM-w1: 7
- PPM-w3: 1
- PPR-pi/2-w1: 6
- PPR-pi/2-w2: 1
- pbc.fabricate: 1
Measurements:
- expval(PauliZ): 1
Depth: Not computed

In the above output, PPR-theta-w<int> denotes the type of PPR present in the circuit, where theta is the PPR angle (\(\theta\)) and w<int> denotes the PPR weight (the number of qubits it acts on, or the length of the Pauli word). PPM-w<int> follows the same convention.

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.