catalyst.passes.cancel_inverses¶
- cancel_inverses(fn=None)[source]¶
Specify that the
-removed-chained-self-inverse
MLIR compiler pass for cancelling two neighbouring self-inverse gates should be applied to the decorated QNode duringqjit()
compilation.The full list of supported gates are as follows:
One-bit Gates:
qml.Hadamard
,qml.PauliX
,qml.PauliY
,qml.PauliZ
Two-bit Gates:
qml.CNOT
,qml.CY
,qml.CZ
,qml.SWAP
Three-bit Gates: -
qml.Toffoli
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 theget_compilation_stage()
function.- Parameters
fn (QNode) – the QNode to apply the cancel inverses compiler pass to
- Return type
QNode
Example
from catalyst.debug import get_compilation_stage from catalyst.passes import cancel_inverses dev = qml.device("lightning.qubit", wires=1) @qjit(keep_intermediate=True) @cancel_inverses @qml.qnode(dev) def circuit(x: float): qml.RX(x, wires=0) qml.Hadamard(wires=0) qml.Hadamard(wires=0) return qml.expval(qml.PauliZ(0))
>>> circuit(0.54) Array(0.85770868, dtype=float64)
Note that the QNode will be unchanged in Python, and will continue to include self-inverse 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(%arg0: tensor<f64>) -> tensor<f64> attributes {llvm.emit_c_interface} { %0 = call @circuit(%arg0) : (tensor<f64>) -> tensor<f64> return %0 : tensor<f64> } func.func private @circuit(%arg0: tensor<f64>) -> tensor<f64> attributes {diff_method = "parameter-shift", llvm.linkage = #llvm.linkage<internal>, qnode} { quantum.device["catalyst/utils/../lib/librtd_lightning.dylib", "LightningSimulator", "{'shots': 0, 'mcmc': False, 'num_burnin': 0, 'kernel_name': None}"] %0 = quantum.alloc( 1) : !quantum.reg %1 = quantum.extract %0[ 0] : !quantum.reg -> !quantum.bit %extracted = tensor.extract %arg0[] : tensor<f64> %out_qubits = quantum.custom "RX"(%extracted) %1 : !quantum.bit %2 = quantum.namedobs %out_qubits[ PauliZ] : !quantum.obs %3 = quantum.expval %2 : f64 %from_elements = tensor.from_elements %3 : tensor<f64> %4 = quantum.insert %0[ 0], %out_qubits : !quantum.reg, !quantum.bit quantum.dealloc %4 : !quantum.reg quantum.device_release return %from_elements : tensor<f64> } func.func @setup() { quantum.init return } func.func @teardown() { quantum.finalize return } }
It can be seen that both Hadamards have been cancelled, and the measurement directly follows the
RX
gate:%out_qubits = quantum.custom "RX"(%extracted) %1 : !quantum.bit %2 = quantum.namedobs %out_qubits[ PauliZ] : !quantum.obs %3 = quantum.expval %2 : f64