qml.transforms.to_zx

to_zx(tape, expand_measurements=False)[source]

This transform converts a PennyLane quantum tape to a ZX-Graph in the PyZX framework. The graph can be optimized and transformed by well-known ZX-calculus reductions.

Parameters:
  • tape (QNode or QuantumTape or Callable or Operation) – The PennyLane quantum circuit.

  • expand_measurements (bool) – The expansion will be applied on measurements that are not in the Z-basis and rotations will be added to the operations.

Returns:

The transformed circuit as described in qml.transform. Executing this circuit will provide the ZX graph in the form of a PyZX graph.

Return type:

graph (pyzx.Graph) or qnode (QNode) or quantum function (Callable) or tuple[List[QuantumTape], function]

Raises:

ModuleNotFoundError – if the required pyzx package is not installed.

Example

You can use the transform decorator directly on your QNode, quantum function and executing it will produce a PyZX graph. You can also use the transform directly on the QuantumTape.

import pyzx
dev = qml.device('default.qubit', wires=2)

@qml.transforms.to_zx
@qml.qnode(device=dev)
def circuit(p):
    qml.RZ(p[0], wires=1),
    qml.RZ(p[1], wires=1),
    qml.RX(p[2], wires=0),
    qml.Z(0),
    qml.RZ(p[3], wires=1),
    qml.X(1),
    qml.CNOT(wires=[0, 1]),
    qml.CNOT(wires=[1, 0]),
    qml.SWAP(wires=[0, 1]),
    return qml.expval(qml.Z(0) @ qml.Z(1))

params = [5 / 4 * np.pi, 3 / 4 * np.pi, 0.1, 0.3]
g = circuit(params)
>>> g
Graph(20 vertices, 23 edges)

It is now a PyZX graph and can apply function from the framework on your Graph, for example you can draw it:

>>> pyzx.draw_matplotlib(g)
<Figure size 800x200 with 1 Axes>

Alternatively you can use the transform directly on a quantum tape and get PyZX graph.

operations = [
        qml.RZ(5 / 4 * np.pi, wires=1),
        qml.RZ(3 / 4 * np.pi, wires=1),
        qml.RX(0.1, wires=0),
        qml.Z(0),
        qml.RZ(0.3, wires=1),
        qml.X(1),
        qml.CNOT(wires=[0, 1]),
        qml.CNOT(wires=[1, 0]),
        qml.SWAP(wires=[0, 1]),
    ]

tape = qml.tape.QuantumTape(operations)
g = qml.transforms.to_zx(tape)
>>> g
Graph(20 vertices, 23 edges)

Here we give an example of how to use optimization techniques from ZX calculus to reduce the T count of a quantum circuit and get back a PennyLane circuit.

Let’s start by starting with the mod 5 4 circuit from a known benchmark library the expanded circuit before optimization is the following QNode:

dev = qml.device("default.qubit", wires=5)

@qml.transforms.to_zx
@qml.qnode(device=dev)
def mod_5_4():
    qml.X(4),
    qml.Hadamard(wires=4),
    qml.CNOT(wires=[3, 4]),
    qml.adjoint(qml.T(wires=[4])),
    qml.CNOT(wires=[0, 4]),
    qml.T(wires=[4]),
    qml.CNOT(wires=[3, 4]),
    qml.adjoint(qml.T(wires=[4])),
    qml.CNOT(wires=[0, 4]),
    qml.T(wires=[3]),
    qml.T(wires=[4]),
    qml.CNOT(wires=[0, 3]),
    qml.T(wires=[0]),
    qml.adjoint(qml.T(wires=[3]))
    qml.CNOT(wires=[0, 3]),
    qml.CNOT(wires=[3, 4]),
    qml.adjoint(qml.T(wires=[4])),
    qml.CNOT(wires=[2, 4]),
    qml.T(wires=[4]),
    qml.CNOT(wires=[3, 4]),
    qml.adjoint(qml.T(wires=[4])),
    qml.CNOT(wires=[2, 4]),
    qml.T(wires=[3]),
    qml.T(wires=[4]),
    qml.CNOT(wires=[2, 3]),
    qml.T(wires=[2]),
    qml.adjoint(qml.T(wires=[3]))
    qml.CNOT(wires=[2, 3]),
    qml.Hadamard(wires=[4]),
    qml.CNOT(wires=[3, 4]),
    qml.Hadamard(wires=4),
    qml.CNOT(wires=[2, 4]),
    qml.adjoint(qml.T(wires=[4]),)
    qml.CNOT(wires=[1, 4]),
    qml.T(wires=[4]),
    qml.CNOT(wires=[2, 4]),
    qml.adjoint(qml.T(wires=[4])),
    qml.CNOT(wires=[1, 4]),
    qml.T(wires=[4]),
    qml.T(wires=[2]),
    qml.CNOT(wires=[1, 2]),
    qml.T(wires=[1]),
    qml.adjoint(qml.T(wires=[2]))
    qml.CNOT(wires=[1, 2]),
    qml.Hadamard(wires=[4]),
    qml.CNOT(wires=[2, 4]),
    qml.Hadamard(wires=4),
    qml.CNOT(wires=[1, 4]),
    qml.adjoint(qml.T(wires=[4])),
    qml.CNOT(wires=[0, 4]),
    qml.T(wires=[4]),
    qml.CNOT(wires=[1, 4]),
    qml.adjoint(qml.T(wires=[4])),
    qml.CNOT(wires=[0, 4]),
    qml.T(wires=[4]),
    qml.T(wires=[1]),
    qml.CNOT(wires=[0, 1]),
    qml.T(wires=[0]),
    qml.adjoint(qml.T(wires=[1])),
    qml.CNOT(wires=[0, 1]),
    qml.Hadamard(wires=[4]),
    qml.CNOT(wires=[1, 4]),
    qml.CNOT(wires=[0, 4]),
    return qml.expval(qml.Z(0))

The circuit contains 63 gates; 28 qml.T() gates, 28 qml.CNOT(), 6 qml.Hadmard() and 1 qml.X(). We applied the qml.transforms.to_zx decorator in order to transform our circuit to a ZX graph.

You can get the PyZX graph by simply calling the QNode:

>>> g = mod_5_4()
>>> pyzx.tcount(g)
28

PyZX gives multiple options for optimizing ZX graphs (pyzx.full_reduce(), pyzx.teleport_reduce(), …). The pyzx.full_reduce() applies all optimization passes, but the final result may not be circuit-like. Converting back to a quantum circuit from a fully reduced graph may be difficult to impossible. Therefore we instead recommend using pyzx.teleport_reduce(), as it preserves the circuit structure.

>>> g = pyzx.simplify.teleport_reduce(g)
>>> pyzx.tcount(g)
8

If you give a closer look, the circuit contains now 53 gates; 8 qml.T() gates, 28 qml.CNOT(), 6 qml.Hadmard() and 1 qml.X() and 10 qml.S(). We successfully reduced the T-count by 20 and have ten additional S gates. The number of CNOT gates remained the same.

The from_zx() transform can now convert the optimized circuit back into PennyLane operations:

tape_opt = qml.transforms.from_zx(g)

wires = qml.wires.Wires([4, 3, 0, 2, 1])
wires_map = dict(zip(tape_opt.wires, wires))
tapes_opt_reorder, fn = qml.map_wires(input=tape_opt, wire_map=wires_map)[0][0]
tape_opt_reorder = fn(tapes_opt_reorder)

@qml.qnode(device=dev)
def mod_5_4():
    for g in tape_opt_reorder:
        qml.apply(g)
    return qml.expval(qml.Z(0))
>>> mod_5_4()
tensor(1., requires_grad=True)

Note

This function is a PennyLane adaptation to circuit_to_graph. It requires the pyzx external package to be installed.

Note

Prior to being added to the graph, Toffoli and CCZ gates are replaced by particular decompositions. These decompositions are described in detail in: J. Welch, A. Bocharov, and K. Svore, “Efficient Approximation of Diagonal Unitaries over the Clifford+T Basis,” Quantum information & computation, vol. 16, Dec. 2014, doi: 10.26421/QIC16.1-2-6. This is necessary because Toffoli and CCZ gates are not directly supported in PyZX.

Copyright (C) 2018 - Aleks Kissinger and John van de Wetering