catalyst.passes.ions_decomposition

ions_decomposition(qnode)[source]

Specify that the --ions-decomposition MLIR compiler pass should be applied to the decorated QNode during qjit() compilation.

This compiler pass decomposes the gates from the set {T, S, PauliZ, Hadamard, PhaseShift, RZ, CNOT} into gates from the set {RX, RY, MS}, where MS is the Mølmer–Sørensen gate, commonly used by trapped-ion quantum devices.

Note

Unlike PennyLane circuit transformations, the QNode itself will not be changed or transformed by applying these decorators.

As a result, circuit inspection tools such as draw() will continue to display the circuit as written in Python.

To instead view the optimized circuit, the MLIR must be viewed after the "QuantumCompilationPass" stage via the get_compilation_stage() function.

Parameters

fn (QNode) – the QNode to apply the ions-decomposition pass to

Return type

QNode

Example

import pennylane as qml
from pennylane.devices import NullQubit

import catalyst
from catalyst import qjit
from catalyst.debug import get_compilation_stage


@qjit(keep_intermediate=True)
@catalyst.passes.ions_decomposition
@qml.qnode(NullQubit(2))
def circuit():
    qml.Hadamard(wires=[0])
    qml.CNOT(wires=[0, 1])
    return qml.expval(qml.PauliY(wires=0))
>>> print(get_compilation_stage(circuit, stage="QuantumCompilationPass"))
module @circuit {
  func.func public @jit_circuit() -> tensor<f64> attributes {llvm.emit_c_interface} {
    %0 = call @circuit_0() : () -> tensor<f64>
    return %0 : tensor<f64>
  }
  func.func public @circuit_0() -> tensor<f64> attributes {diff_method = "parameter-shift", llvm.linkage = #llvm.linkage<internal>, qnode} {
    %c0_i64 = arith.constant 0 : i64
    %cst = arith.constant 0.000000e+00 : f64
    %cst_0 = arith.constant 1.5707963267948966 : f64
    %cst_1 = arith.constant 3.1415926535897931 : f64
    %cst_2 = arith.constant -1.5707963267948966 : f64
    quantum.device shots(%c0_i64) ["catalyst/runtime/build/lib/librtd_null_qubit.so", "NullQubit", "{'shots': 0}"]
    %0 = quantum.alloc( 2) : !quantum.reg
    %1 = quantum.extract %0[ 0] : !quantum.reg -> !quantum.bit
    %out_qubits = quantum.custom "RX"(%cst) %1 : !quantum.bit
    %out_qubits_3 = quantum.custom "RY"(%cst_0) %out_qubits : !quantum.bit
    %out_qubits_4 = quantum.custom "RX"(%cst_1) %out_qubits_3 : !quantum.bit
    %2 = quantum.extract %0[ 1] : !quantum.reg -> !quantum.bit
    %out_qubits_5 = quantum.custom "RY"(%cst_0) %out_qubits_4 : !quantum.bit
    %out_qubits_6:2 = quantum.custom "MS"(%cst_0) %out_qubits_5, %2 : !quantum.bit, !quantum.bit
    %out_qubits_7 = quantum.custom "RX"(%cst_2) %out_qubits_6#0 : !quantum.bit
    %out_qubits_8 = quantum.custom "RY"(%cst_2) %out_qubits_6#1 : !quantum.bit
    %out_qubits_9 = quantum.custom "RY"(%cst_2) %out_qubits_7 : !quantum.bit
    %3 = quantum.namedobs %out_qubits_8[ PauliY] : !quantum.obs
    %4 = quantum.expval %3 : f64
    %from_elements = tensor.from_elements %4 : tensor<f64>
    %5 = quantum.insert %0[ 0], %out_qubits_8 : !quantum.reg, !quantum.bit
    %6 = quantum.insert %5[ 1], %out_qubits_9 : !quantum.reg, !quantum.bit
    quantum.dealloc %6 : !quantum.reg
    quantum.device_release
    return %from_elements : tensor<f64>
  }
  func.func @setup() {
    quantum.init
    return
  }
  func.func @teardown() {
    quantum.finalize
    return
  }
}

You can see that the Hadamard gate has been decomposed to RX(0)RY(pi/2)RX(pi):

%cst = arith.constant 0.000000e+00 : f64
%cst_0 = arith.constant 1.5707963267948966 : f64
%cst_1 = arith.constant 3.1415926535897931 : f64
...
%out_qubits = quantum.custom "RX"(%cst) %1 : !quantum.bit
%out_qubits_3 = quantum.custom "RY"(%cst_0) %out_qubits : !quantum.bit
%out_qubits_4 = quantum.custom "RX"(%cst_1) %out_qubits_3 : !quantum.bit

and that the CNOT gate has been decomposed to its corresponding circuit implementation using the RX, RY and MS gates:

%cst_0 = arith.constant 1.5707963267948966 : f64
%cst_2 = arith.constant -1.5707963267948966 : f64
...
%out_qubits_5 = quantum.custom "RY"(%cst_0) %out_qubits_4 : !quantum.bit
%out_qubits_6:2 = quantum.custom "MS"(%cst_0) %out_qubits_5, %2 : !quantum.bit, !quantum.bit
%out_qubits_7 = quantum.custom "RX"(%cst_2) %out_qubits_6#0 : !quantum.bit
%out_qubits_8 = quantum.custom "RY"(%cst_2) %out_qubits_6#1 : !quantum.bit
%out_qubits_9 = quantum.custom "RY"(%cst_2) %out_qubits_7 : !quantum.bit