catalyst.passes.ppm_compilation¶
- ppm_compilation(qnode=None, *, decompose_method='auto-corrected', avoid_y_measure=False, max_pauli_size=0)[source]¶
Specify that the MLIR compiler pass for transforming clifford+T gates into Pauli Product Measurements (PPM) will be applied.
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_measure
anddecompose_method
arguments are passed to theppr_to_ppm()
pass. Themax_pauli_size
argument is passed to thecommute_ppr()
andmerge_ppr_ppm()
passes.- 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
.max_pauli_size (int) – The maximum size of the Pauli strings after commuting or merging. Defaults to 0 (no limit).
- Returns:
Returns decorated QNode if qnode is provided, otherwise returns a decorator.
- Return type:
QNode or callable
Example
If a merging resulted in a PPM acting on more than
max_pauli_size
qubits (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 from catalyst import qjit, measure from catalyst.passes import ppm_compilation pipeline = [("pipe", ["enforce-runtime-invariants-pipeline"])] method = "clifford-corrected" @qjit(pipelines=pipeline, target="mlir") @ppm_compilation(decompose_method=method, avoid_y_measure=True, 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 measure(0), measure(1) print(circuit.mlir_opt)
Example MLIR Representation:
. . . %m, %out:3 = qec.ppm ["Z", "Z", "Z"] %1, %2, %4 : !quantum.bit, !quantum.bit, !quantum.bit %m_0, %out_1:2 = qec.ppm ["Z", "Y"] %3, %out#2 : !quantum.bit, !quantum.bit %m_2, %out_3 = qec.ppm ["X"] %out_1#1 : !quantum.bit %m_4, %out_5 = qec.select.ppm(%m, ["X"], ["Z"]) %out_1#0 : !quantum.bit %5 = arith.xori %m_0, %m_2 : i1 %6:2 = qec.ppr ["Z", "Z"](2) %out#0, %out#1 cond(%5) : !quantum.bit, !quantum.bit quantum.dealloc_qb %out_5 : !quantum.bit quantum.dealloc_qb %out_3 : !quantum.bit %7 = quantum.alloc_qb : !quantum.bit %8 = qec.fabricate magic_conj : !quantum.bit %m_6, %out_7:2 = qec.ppm ["Z", "Z"] %6#1, %8 : !quantum.bit, !quantum.bit %m_8, %out_9:2 = qec.ppm ["Z", "Y"] %7, %out_7#1 : !quantum.bit, !quantum.bit %m_10, %out_11 = qec.ppm ["X"] %out_9#1 : !quantum.bit %m_12, %out_13 = qec.select.ppm(%m_6, ["X"], ["Z"]) %out_9#0 : !quantum.bit %9 = arith.xori %m_8, %m_10 : i1 %10 = qec.ppr ["Z"](2) %out_7#0 cond(%9) : !quantum.bit quantum.dealloc_qb %out_13 : !quantum.bit quantum.dealloc_qb %out_11 : !quantum.bit %m_14, %out_15:2 = qec.ppm ["Z", "Z"] %6#0, %10 : !quantum.bit, !quantum.bit %from_elements = tensor.from_elements %m_14 : tensor<i1> %m_16, %out_17 = qec.ppm ["Z"] %out_15#1 : !quantum.bit . . .