catalyst.passes.commute_ppr

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

A quantum compilation pass that commutes Clifford Pauli product rotation (PPR) gates, \(\exp(-{iP\tfrac{\pi}{4}})\), past non-Clifford PPRs gates, \(\exp(-{iP\tfrac{\pi}{8}})\), where \(P\) is a Pauli word.

Note

For improved integration with the PennyLane frontend, including inspectability with pennylane.specs(), please use pennylane.transforms.commute_ppr().

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

Note

The commute_ppr compilation pass requires that to_ppr() be applied first.

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

  • max_pauli_size (int) – The maximum size of the Pauli strings after commuting.

Returns:

QNode

Example

The commute_ppr pass must be used in conjunction with to_ppr() to first convert gates into PPRs. In this example, the Clifford+T gates in the circuit will be converted into PPRs first, then the Clifford PPRs will be commuted past the non-Clifford PPR.

import pennylane as qml
import catalyst

p = [("my_pipe", ["quantum-compilation-stage"])]

@qml.qjit(pipelines=p, target="mlir")
@catalyst.passes.commute_ppr
@catalyst.passes.to_ppr
@qml.qnode(qml.device("null.qubit", wires=1))
def circuit():
    qml.H(0)
    qml.T(0)
    return

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:

. . .
%2 = qec.ppr ["X"](8) %1 : !quantum.bit
%3 = qec.ppr ["Z"](4) %2 : !quantum.bit
%4 = qec.ppr ["X"](4) %3 : !quantum.bit
%5 = qec.ppr ["Z"](4) %4 : !quantum.bit
%6 = quantum.insert %0[ 0], %5 : !quantum.reg, !quantum.bit
. . .

If a commutation resulted in a PPR acting on more than max_pauli_size qubits (here, max_pauli_size = 2), that commutation would be skipped.

@qml.qjit(pipelines=p, target="mlir")
@catalyst.passes.commute_ppr(max_pauli_size=2)
@catalyst.passes.to_ppr
@qml.qnode(qml.device("lightning.qubit", wires=3))
def circuit():
    qml.H(0)
    qml.CNOT([1, 2])
    qml.CNOT([0, 1])
    qml.CNOT([0, 2])
    for i in range(3):
        qml.T(i)
    return

print(circuit.mlir_opt)

Example MLIR Representation:

. . .
%4:2 = qec.ppr ["Z", "X"](4) %2, %3 : !quantum.bit, !quantum.bit
%5 = qec.ppr ["X"](8) %1 : !quantum.bit
%6:2 = qec.ppr ["X", "Y"](-8) %5, %4#1 : !quantum.bit, !quantum.bit
%7 = qec.ppr ["X"](-4) %6#1 : !quantum.bit
%8:2 = qec.ppr ["X", "Z"](8) %6#0, %4#0 : !quantum.bit, !quantum.bit
%9 = qec.ppr ["Z"](4) %8#0 : !quantum.bit
%10 = qec.ppr ["X"](4) %9 : !quantum.bit
%11 = qec.ppr ["Z"](4) %10 : !quantum.bit
%12 = qec.ppr ["Z"](-4) %8#1 : !quantum.bit
%13:2 = qec.ppr ["Z", "X"](4) %11, %12 : !quantum.bit, !quantum.bit
%14 = qec.ppr ["X"](-4) %13#1 : !quantum.bit
%15 = qec.ppr ["Z"](-4) %13#0 : !quantum.bit
%16:2 = qec.ppr ["Z", "X"](4) %15, %7 : !quantum.bit, !quantum.bit
%17 = qec.ppr ["Z"](-4) %16#0 : !quantum.bit
%18 = qec.ppr ["X"](-4) %16#1 : !quantum.bit
. . .