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).
Note
For improved integration with the PennyLane frontend, including inspectability with
pennylane.specs(), please usepennylane.transforms.ppm_compilation().This pass combines multiple sub-passes:
to_ppr(): Converts gates into Pauli Product Rotations (PPRs)commute_ppr(): Commutes PPRs past non-Clifford PPRsmerge_ppr_ppm(): Merges PPRs into Pauli Product Measurements (PPMs)ppr_to_ppm(): Decomposes PPRs into PPMs
The
avoid_y_measureanddecompose_methodarguments are passed to theppr_to_ppm()pass. Themax_pauli_sizeargument is passed to thecommute_ppr()andmerge_ppr_ppm()passes.For more information on PPRs and PPMs, check out the Compilation Hub.
- 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."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.max_pauli_size (int) – The maximum size of the Pauli strings after commuting or merging. Defaults to 0 (no limit).
- Returns:
Example
If a merging resulted in a PPM acting on more than
max_pauli_sizequbits (here,max_pauli_size = 2), that merging would be skipped. However, when decomposed into PPMs, at least one qubit will be applied, so the final PPMs will act on at least one additional qubit.import pennylane as qml import catalyst p = [("my_pipe", ["quantum-compilation-stage"])] method = "clifford-corrected" @qml.qjit(pipelines=p, target="mlir") @catalyst.passes.ppm_compilation(decompose_method=method, max_pauli_size=2) @qml.qnode(qml.device("null.qubit", wires=2)) def circuit(): qml.CNOT([0, 1]) qml.CNOT([1, 0]) qml.adjoint(qml.T)(0) qml.T(1) return catalyst.measure(0), catalyst.measure(1) print(circuit.mlir_opt)
Because Catalyst does not currently support execution of Pauli-based computation operations, we must halt the pipeline after
quantum-compilation-stage. This ensures that only the quantum passes will be applied to the initial MLIR, without attempting to further compile for execution.Example MLIR Representation:
. . . %3 = qec.fabricate magic : !quantum.bit %mres, %out_qubits:3 = qec.ppm ["Z", "Z", "Z"] %1, %2, %3 : i1, !quantum.bit, !quantum.bit, !quantum.bit %4 = quantum.alloc_qb : !quantum.bit %mres_0, %out_qubits_1:3 = qec.ppm ["Z", "Z", "Y"](-1) %out_qubits#0, %out_qubits#1, %4 cond(%mres) : i1, !quantum.bit, !quantum.bit, !quantum.bit %mres_2, %out_qubits_3 = qec.ppm ["X"] %out_qubits_1#2 cond(%mres) : i1, !quantum.bit %5 = arith.xori %mres_0, %mres_2 : i1 %6:2 = qec.ppr ["Z", "Z"](2) %out_qubits_1#0, %out_qubits_1#1 cond(%5) : !quantum.bit, !quantum.bit quantum.dealloc_qb %out_qubits_3 : !quantum.bit %mres_4, %out_qubits_5 = qec.ppm ["X"] %out_qubits#2 : i1, !quantum.bit %7:2 = qec.ppr ["Z", "Z"](2) %6#0, %6#1 cond(%mres_4) : !quantum.bit, !quantum.bit quantum.dealloc_qb %out_qubits_5 : !quantum.bit %8 = qec.fabricate magic_conj : !quantum.bit %mres_6, %out_qubits_7:2 = qec.ppm ["Z", "Z"] %7#1, %8 : i1, !quantum.bit, !quantum.bit %9 = quantum.alloc_qb : !quantum.bit %mres_8, %out_qubits_9:2 = qec.ppm ["Z", "Y"](-1) %out_qubits_7#0, %9 cond(%mres_6) : i1, !quantum.bit, !quantum.bit %mres_10, %out_qubits_11 = qec.ppm ["X"] %out_qubits_9#1 cond(%mres_6) : i1, !quantum.bit %10 = arith.xori %mres_8, %mres_10 : i1 %11 = qec.ppr ["Z"](2) %out_qubits_9#0 cond(%10) : !quantum.bit quantum.dealloc_qb %out_qubits_11 : !quantum.bit %mres_12, %out_qubits_13 = qec.ppm ["X"] %out_qubits_7#1 : i1, !quantum.bit %12 = qec.ppr ["Z"](2) %11 cond(%mres_12) : !quantum.bit quantum.dealloc_qb %out_qubits_13 : !quantum.bit %mres_14, %out_qubits_15:2 = qec.ppm ["Z", "Z"] %7#0, %12 : i1, !quantum.bit, !quantum.bit %mres_16, %out_qubits_17 = qec.ppm ["Z"] %out_qubits_15#1 : i1, !quantum.bit . . .