catalyst.passes.gridsynth

gridsynth(qnode=None, *, epsilon=0.0001, ppr_basis=False)[source]

A quantnum compilation pass to discretize single-qubit RZ and PhaseShift gates into the Clifford+T basis or the PPR basis using the Ross-Selinger Gridsynth algorithm. Reference: https://arxiv.org/abs/1403.2975

Note

The actual discretization is only performed during execution time.

Parameters:
  • qnode (QNode) – the QNode to apply the gridsynth compiler pass to

  • epsilon (float) – The maximum permissible operator norm error per rotation gate. Defaults to 1e-4.

  • ppr_basis (bool) – If true, decompose directly to Pauli Product Rotations (PPRs) in QEC dialect. Defaults to False

Returns:

QNode

Note

The circuit generated from this pass with ppr_basis=True are currently not executable on any backend. This is only for analysis with the null.qubit device and potential future execution when a suitable backend is available.

Example

In this example the RZ gate will be converted into a new function, which calls the discretization at execution time.

import pennylane as qml
from catalyst import qjit
from catalyst.passes import gridsynth

pipe = [("pipe", ["quantum-compilation-stage"])]


@qjit(pipelines=pipe, target="mlir")
@gridsynth
@qml.qnode(qml.device("null.qubit", wires=1))
def circuit():
    qml.RZ(x, wires=0)
    return qml.probs()

>>> print(circuit.mlir_opt)

Example MLIR Representation:

. . .
func.func private @rs_decomposition_get_phase(f64, f64, i1) -> f64
func.func private @rs_decomposition_get_gates(memref<?xindex>, f64, f64, i1)
func.func private @rs_decomposition_get_size(f64, f64, i1) -> index
func.func private @__catalyst_decompose_RZ_0(%arg0: !quantum.bit, %arg1: f64) -> (!quantum.bit, f64) {
    . . .
    %2 = scf.for %arg2 = %c0 to %0 step %c1 iter_args(%arg3 = %arg0) -> (!quantum.bit) {
        %3 = memref.load %alloc[%arg2] : memref<?xindex>
        %4 = scf.index_switch %3 -> !quantum.bit
        case 0 {
            %out_qubits = quantum.custom "T"() %arg3 : !quantum.bit
            scf.yield %out_qubits : !quantum.bit
        }
        case 1 {
            %out_qubits = quantum.custom "Hadamard"() %arg3 : !quantum.bit
            %out_qubits_0 = quantum.custom "T"() %out_qubits : !quantum.bit
            scf.yield %out_qubits_0 : !quantum.bit
        }
        case 2 {
            %out_qubits = quantum.custom "S"() %arg3 : !quantum.bit
            %out_qubits_0 = quantum.custom "Hadamard"() %out_qubits : !quantum.bit
            %out_qubits_1 = quantum.custom "T"() %out_qubits_0 : !quantum.bit
            scf.yield %out_qubits_1 : !quantum.bit
        }
        . . .
    }
}

func.func public @circuit_0(%arg0: tensor<f64>) -> tensor<f64> attributes {diff_method = "adjoint", llvm.linkage = #llvm.linkage<internal>, qnode} {
    . . .
    %2:2 = call @__catalyst_decompose_RZ_0(%1, %extracted) : (!quantum.bit, f64) -> (!quantum.bit, f64)
    . . .
}