qp.transforms.cancel_inverses¶
- cancel_inverses(tape, recursive=True)[source]¶
Quantum function transform to remove any operations that are applied next to their (self-)inverses or adjoint.
- Parameters:
tape (QNode or QuantumTape or Callable) – A quantum circuit (QNode or quantum function).
recursive (bool) –
Whether or not to recursively cancel inverses after a first pair of mutual inverses has been cancelled. Enabled by default.
Note
This argument is not supported within a
qjit()workflow.
- Returns:
The transformed circuit as described in
qp.transform.- Return type:
qnode (QNode) or quantum function (Callable) or tuple[List[QuantumTape], function]
Example
You can apply it on quantum functions:
def qfunc(x, y, z): qp.Hadamard(wires=0) qp.Hadamard(wires=1) qp.Hadamard(wires=0) qp.RX(x, wires=2) qp.RY(y, wires=1) qp.X(1) qp.RZ(z, wires=0) qp.RX(y, wires=2) qp.CNOT(wires=[0, 2]) qp.X(1) return qp.expval(qp.Z(0))
The circuit before optimization:
>>> dev = qp.device("default.qubit") >>> qnode = qp.QNode(qfunc, dev) >>> print(qp.draw(qnode)(1, 2, 3)) 0: ──H─────────H─────────RZ(3.00)─╭●────┤ <Z> 1: ──H─────────RY(2.00)──X────────│───X─┤ 2: ──RX(1.00)──RX(2.00)───────────╰X────┤
We can see that there are two adjacent Hadamards on the first qubit that should cancel each other out. Similarly, there are two
Xgates on the second qubit that should cancel. We can obtain a simplified circuit by running thecancel_inversestransform:>>> optimized_qnode = qp.transforms.cancel_inverses(qnode) >>> print(qp.draw(optimized_qnode)(1, 2, 3)) 0: ──RZ(3.00)───────────╭●─┤ <Z> 1: ──H─────────RY(2.00)─│──┤ 2: ──RX(1.00)──RX(2.00)─╰X─┤
Usage with qjit
There are three key differences to note when using
cancel_inverseswithqjit:cancel_inversesmust be applied to a QNode. Quantum functions are not supported as input.The
recursiveargument is not supported, and an error will be raised if a value forrecursiveis specified.Only the following gates can be optimized by
cancel_inverseswithqjit:
dev = qp.device("lightning.qubit", wires=1) @qp.qjit(capture=True) @qp.transforms.cancel_inverses @qp.qnode(dev) def circuit(): qp.RX(0.1, wires=0) qp.Hadamard(wires=0) qp.Hadamard(wires=0) return qp.expval(qp.PauliZ(0))
>>> print(qp.specs(circuit, level=1)()) Device: lightning.qubit Device wires: 1 Shots: Shots(total=None) Level: cancel-inverses Wire allocations: 1 Total gates: 1 Gate counts: - RX: 1 Measurements: - expval(PauliZ): 1 Depth: Not computed
Additionally, the
cancel_inversestransform withqjitsupports loop-boundary optimization.For more technical information on how this transform behaves, consult the Catalyst documentation for
catalyst.passes.cancel_inverses().