catalyst.passes.disentangle_cnot

disentangle_cnot(qnode)[source]

Specify that the -disentangle-CNOT MLIR compiler pass for simplifying CNOT gates should be applied to the decorated QNode during qjit() compilation.

Parameters:

fn (QNode) – the QNode to apply the disentangle CNOT compiler pass to

Return type:

QNode

Example

import pennylane as qml
from catalyst import qjit
from catalyst.debug import get_compilation_stage
from catalyst.passes import disentangle_cnot

dev = qml.device("lightning.qubit", wires=2)

@qjit(keep_intermediate=True)
@disentangle_cnot
@qml.qnode(dev)
def circuit():
    # first qubit in |1>
    qml.X(0)
    # second qubit in |0>
    # current state : |10>
    qml.CNOT([0,1]) # state after CNOT : |11>
    return qml.state()
>>> circuit()
[0.+0.j  0.+0.j  0.+0.j  1.+0.j]

Note that the QNode will be unchanged in Python, and will continue to include keep CNOT gates gates when inspected with Python (for example, with draw()).

To instead view the optimized circuit, the MLIR must be viewed after the "QuantumCompilationPass" stage:

>>> print(get_compilation_stage(circuit, stage="QuantumCompilationPass"))
module @circuit {
    func.func public @jit_circuit() -> tensor<4xcomplex<f64>> attributes {llvm.emit_c_interface} {
        %0 = call @circuit_0() : () -> tensor<4xcomplex<f64>>
        return %0 : tensor<4xcomplex<f64>>
    }
    func.func public @circuit_0() -> tensor<4xcomplex<f64>> attributes {diff_method = "parameter-shift", llvm.linkage = #llvm.linkage<internal>, qnode} {
        %c0_i64 = arith.constant 0 : i64
        quantum.device["catalyst/utils/../lib/librtd_lightning.dylib", "LightningSimulator", "{'shots': 0, 'mcmc': False, 'num_burnin': 0, 'kernel_name': None}"]
        %0 = quantum.alloc( 2) : !quantum.reg
        %1 = quantum.extract %0[ 0] : !quantum.reg -> !quantum.bit
        %out_qubits = quantum.custom "PauliX"() %1 : !quantum.bit
        %2 = quantum.extract %0[ 1] : !quantum.reg -> !quantum.bit
        %out_qubits_0 = quantum.custom "PauliX"() %2 : !quantum.bit
        %3 = quantum.insert %0[ 0], %out_qubits : !quantum.reg, !quantum.bit
        %4 = quantum.insert %3[ 1], %out_qubits_0 : !quantum.reg, !quantum.bit
        %5 = quantum.compbasis qreg %4 : !quantum.obs
        %6 = quantum.state %5 : tensor<4xcomplex<f64>>
        quantum.dealloc %4 : !quantum.reg
        quantum.device_release
        return %6 : tensor<4xcomplex<f64>>
    }
    func.func @setup() {
        quantum.init
        return
    }
    func.func @teardown() {
        quantum.finalize
        return
    }
}

It can be seen that the CNOT(0,1) has been replaced with X(1)

%2 = quantum.extract %0[ 1] : !quantum.reg -> !quantum.bit
%out_qubits_0 = quantum.custom "PauliX"() %2 : !quantum.bit