catalyst.passes.ppr_to_ppm

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

Specify that the MLIR compiler passes for decomposing Pauli Product rotations (PPR) , \(\exp(-iP\theta)\), into Pauli Pauli measurements (PPM) will be applied.

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, and then Clifford PPRs (\(\theta = \tfrac{\pi}{4}\)) are decomposed. Non-Clifford decomposition can be performed in one of two ways: "clifford-corrected" or "auto-corrected", by default the latter is used. Both methods are based on A Game of Surface Codes, figures 7 and 17(b) respectively.

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 "auto-corrected" and "clifford-corrected". Defaults to "auto-corrected". "auto-corrected" uses an additional measurement for correction. "clifford-corrected" uses a Clifford rotation for correction.

  • 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). Defaults to False.

Returns:

Returns decorated QNode if qnode is provided,

otherwise returns a decorator.

Return type:

QNode or callable

Example

This example shows the sequence of passes that will be applied. The last pass will convert the non-Clifford PPR into Pauli Product Measurements.

import pennylane as qml
from catalyst import qjit, measure
from catalyst.passes import to_ppr, commute_ppr, merge_ppr_ppm, ppr_to_ppm

pipeline = [("pipe", ["enforce-runtime-invariants-pipeline"])]

@qjit(pipelines=pipeline, target="mlir")
@to_ppr
@commute_ppr
@merge_ppr_ppm
@ppr_to_ppm
@qml.qnode(qml.device("null.qubit", wires=2))
def circuit():
    qml.H(0)
    qml.T(0)
    qml.CNOT([0, 1])
    return measure(0), measure(1)

print(circuit.mlir_opt)

Example MLIR Representation:

. . .
%5 = qec.fabricate  zero : !quantum.bit
%6 = qec.fabricate  magic : !quantum.bit
%mres, %out_qubits:2 = qec.ppm ["X", "Z"] %1, %6 : !quantum.bit, !quantum.bit
%mres_0, %out_qubits_1:2 = qec.ppm ["Z", "Y"] %5, %out_qubits#1 : !quantum.bit, !quantum.bit
%mres_2, %out_qubits_3 = qec.ppm ["X"] %out_qubits_1#1 : !quantum.bit
%mres_4, %out_qubits_5 = qec.select.ppm(%mres, ["X"], ["Z"]) %out_qubits_1#0 : !quantum.bit
%7 = arith.xori %mres_0, %mres_2 : i1
%8 = qec.ppr ["X"](2) %out_qubits#0 cond(%7) : !quantum.bit
. . .