Grishma Prasad
11 min readDec 12, 2022

--

ZX-Calculus, Qiskit Transpiler Pass, pyZX and my QAMP Fall 2022 experience

A few months ago, I had come across a paper on Quantum Picturalism. I was fascinated by a completely different approach of thinking about and visualizing Quantum Circuits. Ever since I got to know about something called ZX-calculus (which is quite some time ago), I was interested in understanding this topic, but I never really tried going deeper into it. After I registered for the Qiskit Advocate Mentorship Program, I found one of the projects was related to creating a Qiskit transpiler pass that involved bringing in ZX-calculus specific optimizations to Quantum circuits in Qiskit, I opted for it because I could learn more about ZX-calculus and also get more involved in Open source contributions in Qiskit-Terra (if not within QAMP, shortly after that). I am glad I got the chance to work on it. I will be talking about ZX-calculus and Qiskit transpiler passes soon in this blog, but before I do that, I must tell you that QAMP has been a great experience and one reason (out of several others) to want to become a Qiskit Advocate is that one can be a part of QAMP. The process involves registering for the program via a form in which one has to mention 2 most preferred projects (we had to choose projects from here.​) and provide reasons why one wants to work on them. One gets to work on one of the 2 chosen ones. So now, let’s understand ZX-calculus and transpiler passes a little.

My main resource for understanding ZX-calculus was the original ZX calculus paper itself. Before I go ahead and explain what ZX-calculus is, one question that many of us might have is, why do we even need a different way to look at Quantum circuits? and that’s definitely a question I had too. In my opinion, one of the best uses of ZX-calculus is in teaching and explaining Quantum Computing (eg: https://github.com/JavaFXpert/learning-qc-with-zx). If we try understanding the basis of ZX-calculus, I am guessing that many of us would agree to this point. I would also like to suggest this blog that I very recently read. But beyond teaching, there are some different types of optimizations that ZX-calculus offers, mainly because one can think of ZX-diagrams as graphs and Graph Theory becomes applicable. I will briefly explain this and point to relevant sections in the paper.

What are ZX-diagrams? They are compositions of ‘spiders’. Seriously!! Okay, let me explain ZX-calculus spiders. Spiders are the basic building blocks of ZX-diagrams. The below picture explains what it is mathematically in ZX-calculus:

So in general, a spider in ZX-calculus would look like:

The wires in the left are called inputs and the wires to the right depict the output. If someone has been looking at circuits in Qiskit or any other framework, it can seem a little non-intuitive that one can have different number of inputs and outputs. This was definitely not intuitive to me at all because in a normal circuit diagram, even if we think of initial state as the input with n qubits and then, no matter what gates we apply, we would end up with a state which again has n qubits only. Here’s one important point, the purpose of ZX-diagrams is to go beyond circuits and it basically depicts any linear map from n inputs to m outputs, but one can still actually have a mapping from ZX-diagrams to Quantum Circuits and if we see the first picture I pasted here, it should be kind of clear. We can see that when we have a single output an no input, it is called initialization and if we have ever read about the implementations of algorithms like Grover’s, we know we have what we know as ancilla or auxiliary qubits. The purpose of the auxiliary qubit is that it initialized to say, |-> state and then, we use it as a target qubit for some controlled gates in such a way that a phase is kicked back. A single output and no input spider is like this auxiliary qubit. Similarly, a single input and no output spider is like a measured qubit before any operations. So, suppose in a ZX-diagram, we have r outputs that have no inputs associated to them, then, those r outputs form the auxiliary qubits and suppose we have t inputs with no outputs, it means t qubits are measured in the beginning of any operations in the circuit.

Why do we have these colors red and green? A green spider is called a Z-spider, which essentially means a Z-phase operation and a red spider is called a X-spider which means it is a X-phase operation. In the paper, they have used a white color to depict a Z-spider and a gray/blackish color to depict a X-spider, so that even those who cannot distinguish between red and green can understand when we refer to a X-spider and when we refer to a Z-spider. X-spiders and Z-spiders should make us think that these are referring to rotations about X and Z axes about the Bloch sphere, respectively. Why don’t we have a Y-spider? that’s because we can write a Y-rotation in terms of X and Z rotations. We could have very well had a ZY-calculus or XY-calculus, but there is a good reason to consider ZX-calculus instead, which has been explained in the paper. The eigenvectors of X and Z Pauli matrices are self-conjugate meaning that each of the vectors in their eigen-basis is equal to its own component-wise complex conjugate. This makes it simpler to work with ZX-calculus. Let me put the mathematical definitions of X and Z spiders from the paper:

Some other important aspects of ZX diagrams is that a straight wire with no inputs and outputs is the Identity (I). Another important concept is that of composition of spiders. Stacking ZX-diagrams means we would be taking a tensor product whereas connecting the balls of spiders or diagrams means we would do the actual matrix multiplication. I would highly recommend section 3.2 of the paper where they have very nicely derived the CNOT gate using compositions of spiders. Other useful diagrams are the cups and caps.

The first is a cup and if we see, it is actually the Bell state and the cap is just a transpose of the cup and is called the Bell effect.

The other property of ZX-diagrams is that one can actually bend the wires anyhow and they would still represent the same diagram and the same matrix. When we bend wires, we are essentially performing compositions between the original diagram and cups or caps. This was non-intuitive to me because if we just compose with cups and caps, we can get different matrices. I had to sit with a pen and paper to convince myself about this and I can show an example that I tried myself. The point to note is that one can always use the single wire denoting Identity, just like the CNOT case explained the paper.

The other property of ZX-diagrams is that one can actually bend the wires anyhow and they would still represent the same diagram and the same matrix. We just need to ensure that the connections don’t change. When we bend wires, we are essentially performing compositions between the original diagram and cups or caps. This was non-intuitive to me because if we just compose with cups and caps, we can get different matrices. I had to sit with a pen and paper to convince myself about this and I can show an example that I tried myself. The point to note is that one can always use the single wire denoting Identity, just like the CNOT case explained the paper.

With ZX-calculus, there are several rules that are actually quite intuitive. With these rules, a lot of circuit identities can easily be proved.

One such rule is spider fusion. Suppose, I perform a rotation of angle x1 followed by a rotation of angle x2 about X-axis, this is equivalent to performing a rotation of angle x1+x2 about X-axis. This is exactly what spider fusion is all about. We can similarly conclude for rotations about Z-axis.

I mentioned before that a single wire with no operations is an Identity. Suppose we have a single input and a single output, 0 phase X or Z spider, that’s clearly no rotation, so those are equivalent to identity:

This can help us remove some unnecessary loops in a ZX-diagram. Example is equation 52 from the paper, which looks like below:

Another rewrite rule is the copy rule. Suppose, we have a Z-spider with single input and no phase. It is basically |0···0⟩⟨0| + |1···1⟩⟨1|. If we apply an X gate to the input, we would get |0 · ·· 0⟩⟨0|X + |1···1⟩⟨1|X = |0···0⟩⟨1| + |1···1⟩⟨0|. But if we see carefully, this is equivalent to: (X ⊗ ··· ⊗ X)|1···1⟩⟨1| + (X ⊗···⊗X)|0···0⟩⟨0|. This is exactly the copy rule, in terms of ZX-diagrams, it would look like:

The other important rule is the color change from red to green and vice-versa or gray to while and vice-versa. If we know about the well-known single qubit gates, we know a Hadamard gate can help us with it. A Hadamard gate is represented as square in ZX-calculus. Equation 62 from the paper:

There are other re-write rules as well and they are really explained very well in the paper. I really liked reading and understanding the Bialgebra and Hoph rule (section 4.5 of the paper).

To understand the Circuit/Diagram/Graph optimizations offered by ZX-calculus, in-depth, there are more concepts that need to be understood. I can keep writing about my understanding of those, but I feel it can be best understood from the paper itself, but I am of course very much in for discussions. These are some sections from the paper that I would like to highlight: 5.6, 6.1, 6.2, 6.4, 6.5, 6.6. Now, let’s discuss the Qiskit Transpiler Pass.

For the 1st checkpoint in QAMP, my tasks were to mainly understand ZX-calculus and the Qiskit Transpiler pass. We had to share a presentation, which I did here. My main resource to understand Qiskit transpilers was this tutorial: https://qiskit.org/documentation/tutorials/circuits_advanced/04_transpiler_passes_and_passmanager.html The tutorial shows an example of how a custom transpiler pass is created. As mentioned in this tutorial : ‘A central component of Qiskit Terra is the transpiler, which is designed for modularity and extensibility. The goal is to be able to easily write new circuit transformations (known as transpiler passes), and combine them with other existing passes. Which passes are chained together and in which order has a major effect on the final outcome. This pipeline is determined by a pass manager, which schedules the passes and also allows passes to communicate with each other by providing a shared space. In this way, the transpiler opens up the door for research into aggressive optimization of quantum circuits.’ I started to look at the implementations of transpiler passes in Qiskit to get an even better idea. To develop a custom transpiler pass, it is useful to understand that in Qiskit, the internal representation of circuits we build and see, are actually in the form of a DAG (Directed Acyclic Graph). For a simple circuit like below:

the below is the corresponding DAG:

The green nodes denote inputs, red ones denote outputs and the blue nodes denote gates/operations. To me, this is very similar to how we just described a ZX-diagram, we have inputs and outputs. The only difference is that in case of Qiskit DAGs, we are sure to have the same number of input nodes as the number of output nodes.

Now my task for QAMP was to build a custom transpiler pass that could from a given ZX-diagram, perform all the ZX-calculus specific optimizations and give out the corresponding Qiskit DAG, and also the vice-versa, where a given DAG circuit would go through the ZX optimizations and we could get the resultant ZX-diagram. One popular implementation of ZX-calculus is pyZX: https://github.com/Quantomatic/pyzx , https://pyzx.readthedocs.io/en/latest/ By the 2nd checkpoint of QAMP, I managed to implement the custom transpiler pass (https://github.com/gprs1809/ZX_to_DAG , I will be updating this repository today). We also had to write about it. One can check my write-up about the implementation here: https://github.com/qiskit-advocate/qamp-fall-22/issues/27#issuecomment-1299529966 . After checkpoint 2, I started learning about unittesting in Python. My main resources were: some examples from https://github.com/Qiskit/qiskit-terra/tree/main/test/python/transpiler and https://realpython.com/python-testing/ I have always tried testing my code, but this formal approach to testing was new to me and I realized its importance while performing unittesting. Our testing approach was to check if the matrix corresponding to our input and the unitary matrix we get from the output, are actually equal, up to a global phase. We realized that for some cases, the unitary matrix changes and I raised an issue: https://github.com/Quantomatic/pyzx/issues/102 . I had first raised this question on the ZX-calculus discord server and they too felt that it could be a bug in pyZX. We decided that our next step would be to do some corrections in pyZX and contribute there and then, further work on our transpiler pass. My mentor in QAMP also shared the pyZX implementation with Rust that I am keen on looking at.

Overall, I am happy with the experience and learning. I wish our work could have got merged in Qiskit-Terra by the end of QAMP, but that can’t happen by the end of QAMP because of the above issue, but I am hoping that we can eventually push it and it gets merged. The final showcase was on 8th December and I was so amazed at the wonderful projects everyone worked on! :)

--

--

Grishma Prasad

I am deeply passionate about learning and doing everything in complete depth. My interests are Science, Technology and Mathematics.