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.Warning
Currently, only Hadamard gates are canceled.
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.- 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