catalyst.passes.reduce_t_depth

reduce_t_depth(qnode)[source]

An MLIR compiler pass that reduces the depth and count of non-Clifford PPRs (e.g., T gates) by commuting PPRs in adjacent layers and merging compatible ones (a layer comprises a set of PPRs that mutually commute). For more details, see the Figure 6 of A Game of Surface Codes.

The impact can be measured using catalyst.passes.ppm_specs to compare the circuit depth before and after applying the pass. The ppm_specs function provides detailed statistics including depth_pi8_ppr (non-Clifford PPR depth) and pi8_ppr (number of non-Clifford PPRs), allowing users to quantify the optimization achieved by the pass.

Parameters:

qnode (QNode) – QNode to apply the pass to.

Returns:

Returns decorated QNode.

Return type:

QNode

Example

In the example below, after performing the catalyst.passes.to_ppr() and catalyst.passes.merge_ppr_ppm() passes, the circuit contains a depth of four of non-Clifford PPRs. Subsequently applying the reduce_t_depth pass will move PPRs around via commutation, resulting in a circuit with a smaller PPR depth. Specifically, in the circuit below, the Pauli-\(X\) PPR (\(\exp(iX\tfrac{\pi}{8})\)) on qubit Q1 will be moved to the first layer, which results in a depth of three non-Clifford PPRs.

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

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


@qjit(pipelines=pips, target="mlir")
@reduce_t_depth
@merge_ppr_ppm
@commute_ppr
@to_ppr
@qml.qnode(qml.device("null.qubit", wires=3))
def circuit():
    n = 3
    for i in range(n):
        qml.H(wires=i)
        qml.S(wires=i)
        qml.CNOT(wires=[i, (i + 1) % n])
        qml.T(wires=i)
        qml.H(wires=i)
        qml.T(wires=i)

    return [measure(wires=i) for i in range(n)]


print(circuit.mlir_opt)

Example MLIR Representation:

. . .
%1 = quantum.extract %0[ 0] : !quantum.reg -> !quantum.bit
%2 = quantum.extract %0[ 1] : !quantum.reg -> !quantum.bit
// layer 1
%3 = qec.ppr ["X"](8) %1 : !quantum.bit
%4 = qec.ppr ["X"](8) %2 : !quantum.bit

// layer 2
%5 = quantum.extract %0[ 2] : !quantum.reg -> !quantum.bit
%6:2 = qec.ppr ["Y", "X"](8) %3, %4 : !quantum.bit, !quantum.bit
%7 = qec.ppr ["X"](8) %5 : !quantum.bit
%8:3 = qec.ppr ["X", "Y", "X"](8) %6#0, %6#1, %7:!quantum.bit, !quantum.bit, !quantum.bit

// layer 3
%9:3 = qec.ppr ["X", "X", "Y"](8) %8#0, %8#1, %8#2:!quantum.bit, !quantum.bit, !quantum.bit
. . .