qml.transforms.parity_synth

parity_synth(tape)[source]

Pass for applying ParitySynth to phase polynomials in a circuit.

ParitySynth has been proposed by Vandaele et al. in arXiv:2104.00934 as a technique to synthesize phase polynomials into elementary quantum gates, namely CNOT and RZ. For this, it synthesizes the parity table of the phase polynomial, and defers the remaining parity matrix synthesis to RowCol.

This pass walks over the input circuit and aggregates all CNOT and RZ operators into a subcircuit that describes a phase polyonomial. Other gates form the boundaries of these subcircuits, and whenever one is encountered the phase polynomial of the aggregated subcircuit is resynthesized with the ParitySynth algorithm. This implies that while this pass works on circuits containing any operations, it is recommended to maximize the subcircuits that represent phase polynomials (i.e. consist of CNOT and RZ gates) to enhance the effectiveness of the pass. This might be possible through decomposition or re-ordering of commuting gates. Note that higher-level program structures, such as nested functions and control flow, are synthesized independently, i.e., boundaries of such structures are always treated as boundaries of phase polynomial subcircuits as well. Similarly, dynamic wires create boundaries around the operations using them, potentially causing the separation of consecutive phase polynomial operations into multiple subcircuits.

Example

In the following, we apply the pass to a simple quantum circuit that has optimization potential in terms of commuting gates that can be interchanged to unlock a cancellation of a self-inverse gate (CNOT) with itself. Concretely, the circuit is:

import pennylane as qml

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

@qml.qnode(dev)
def circuit(x: float, y: float, z: float):
    qml.CNOT((0, 1))
    qml.RZ(x, 1)
    qml.CNOT((0, 1))
    qml.RX(y, 1)
    qml.CNOT((1, 0))
    qml.RZ(z, 1)
    qml.CNOT((1, 0))
    return qml.state()

We can draw the circuit and observe the last RZ gate being wrapped in a pair of CNOT gates that commute with it:

>>> print(qml.draw(circuit)(0.52, 0.12, 0.2))
0: ─╭●───────────╭●───────────╭X───────────╭X─┤  State
1: ─╰X──RZ(0.52)─╰X──RX(0.12)─╰●──RZ(0.20)─╰●─┤  State

Now we apply the parity_synth_pass to the circuit and quantum just-in-time (qjit) compile the circuit into a reduced MLIR module:

qjit_circuit = qml.qjit(qml.transforms.parity_synth(circuit))
specs = qml.specs(qjit_circuit, level="device")(0.52, 0.12, 0.2)

Looking at the resources of the compiled module, we find only five gates left in the program; the CNOTs have been cancelled successfully.

>>> print(specs.resources["gate_types"])
{'RX': 1, 'RZ': 2, 'CNOT': 2}

Note that for this circuit, ParitySynth is run twice; once for the first three gates and once for the last three gates. This is because RX is not a phase polynomial operation, so that it forms a boundary for the phase polynomial subcircuits that are re-synthesized by the pass.