Quantum Circuit Plotting with SymPy

Rick Muller, [email protected]

Explore what can and cannot be done using existing sympy circuit plotting. The initial aim will be to support everything that is on the qasm2circ website. When you see a heading listing an example number, it will refer to the example circuit on that page.

Note: The examples on this page will require at least the development (i.e. github) branch of sympy, and may require work in my current branch (which changes from week to week). If you'd like to play with newer features, and the examples on this page aren't working for you, email me ([email protected]) and I can send you instructions for how to get these to work.

In [1]:
from sympy import *
from sympy.physics.quantum.circuitplot import CircuitPlot,labeller,Mz,CreateOneQubitGate
from sympy.physics.quantum.gate import *
from sympy.physics.quantum.qasm import Qasm

Rough priorities of tasks

  • Stack multiple gates vertically
  • Block multiqubit gate
  • Latex matrices as gates
  • Do circuits resize correctly?

Qasm tests status

  1. Works
  2. Works
  3. Works
  4. Works
  5. Can't plot matrices as operators
  6. Works
  7. Works
  8. Works
  9. Works
  10. Multi-qubit blocks + classical lines
  11. Multi-qubit blocks
  12. Multi-qubit blocks
  13. Multi-qubit blocks (kinda works if you convert defbox->def by hand)
  14. Multi-qubit blocks
  15. D-shaped measurement
  16. D-shaped measurement + slash gate + vertical line gates
  17. D-shaped measurement

X gate test

In [2]:
CircuitPlot(CNOT(1,0),2)
Out[2]:
<sympy.physics.quantum.circuitplot.CircuitPlot at 0x103844f90>
In [3]:
CircuitPlot(X(0),1)
Out[3]:
<sympy.physics.quantum.circuitplot.CircuitPlot at 0x104dec090>

Sympy now prints a normal X gate when it's a one-qubit gate, but the circ+ when it's a CNOT!

Z gate test

In [4]:
CircuitPlot(CPHASE(1,0),2)
Out[4]:
<sympy.physics.quantum.circuitplot.CircuitPlot at 0x104e03d10>
In [5]:
CircuitPlot(Z(0),1)
Out[5]:
<sympy.physics.quantum.circuitplot.CircuitPlot at 0x104ecba90>

Making arbitrary gates:

It's pretty easy to overload single-qubit operators, and you can even specify fairly complicated LaTeX:

In [6]:
VGate = CreateOneQubitGate('V')
SqrtX = CreateOneQubitGate('sqrt-X','\sqrt{X}')
In [7]:
CircuitPlot(SqrtX(0)*VGate(0),1)
Out[7]:
<sympy.physics.quantum.circuitplot.CircuitPlot at 0x104ee3b10>

You can even make these controlled:

In [8]:
CircuitPlot(CGate(1,SqrtX(0)),2)
Out[8]:
<sympy.physics.quantum.circuitplot.CircuitPlot at 0x104e7d690>

Unfortunately, there isn't a general way to make a block multi-qubit gate. You need to overload the plot_gate function, which I haven't. And, I guess you also have to make sure that all of the qubits are contiguous.

Todo Create multi-qubit gate.

In [9]:
class SqrtSWAP(TwoQubitGate):
    gate_name = 'sqrt-SWAP'
    gate_name_latex = u'\sqrt{SWAP}'
In [10]:
# This doesn't work:
# CircuitPlot(SqrtSWAP(0,1),2)

Example 1: EPR Creation

Goal:

In [11]:
CircuitPlot(CNOT(1,0)*H(1),2,labels=labeller(2))
Out[11]:
<sympy.physics.quantum.circuitplot.CircuitPlot at 0x104ef0e50>

A couple of notes:

  • The top wire in a figure is the nth qubit in the circuit, not the 0-th or 1st.
  • The gates in a circuit are ordered left to right. In other words, the first gate to be applied is the left-most gate in the circuit.

Both of these make perfect sense from a physics point of view, but they may not be what you expect.

qasm version:

In [12]:
q = Qasm('qubit q0','qubit q1','h q0','cnot q0,q1')
q.plot()

Works, but note that I haven't implemented the subscripts in qasm parser yet. It's not hard to rewrite the labelling so that it sticks an underscore between the alphabetical characters and the numbers, but I don't want to limit the way the code works based on assumptions of use cases that I haven't fully thought through. In the meantime, if you stick underscores in the label names, these will work:

In [13]:
q = Qasm('qubit q_0','qubit q_1','h q_0','cnot q_0,q_1')
q.plot()

You can also call the commands directly:

In [14]:
q = Qasm()
q.qubit('q_0')
q.qubit('q_1')
q.h('q_0')
q.cnot('q_0','q_1')
q.plot()

Example 2: Teleportation Gate

Goal:

In [15]:
circuit = CGate(2,Z(0))*CGate(1,X(0))*Mz(2)*Mz(1)*H(2)*CNOT(2,1)*CNOT(1,0)*H(1)
cp = CircuitPlot(circuit,3,labels=labeller(3))
In [16]:
# qasm version
q = Qasm('qubit q_0','qubit q_1','qubit q_2','h  q_1',
         'cnot q_1,q_2','cnot q_0,q_1','h q_0',
         'measure q_1','measure q_0',
         'c-x q_1,q_2','c-z q_0,q_2')
q.plot()

Example 3: Swap Gate

Goal:

In [17]:
CircuitPlot(CNOT(1,0)*CNOT(0,1)*CNOT(1,0),2,labels=labeller(2))
Out[17]:
<sympy.physics.quantum.circuitplot.CircuitPlot at 0x104fae810>

qasm version:

In [18]:
q = Qasm('qubit q_0','qubit q_1','cnot q_0,q_1','cnot q_1,q_0','cnot q_0,q_1')
q.plot()

Example 4: QFT on 3 Qubits

Goal:

In [19]:
CircuitPlot(SWAP(0,2)*H(0)* CGate((0,),S(1)) *H(1)*CGate((0,),T(2))*CGate((1,),S(2))*H(2),3,labels=labeller(3,'j'))
Out[19]:
<sympy.physics.quantum.circuitplot.CircuitPlot at 0x104f7de50>
In [20]:
# qasm version:
q = Qasm("def	c-S,1,'S'",
        "def	c-T,1,'T'",
        "qubit	j0",
        "qubit	j1",
        "qubit	j2",
        "h	j0",
        "c-S	j1,j0",
        "c-T	j2,j0",
        "nop	j1",
        "h	j1",
        "c-S	j2,j1",
        "h	j2",
        "swap	j0,j2")
q.plot()

Example 5

Goal:

In [21]:
# Note: you currently have to escape (double backslash) the \b and \t, 
# I guess b/c they're whitespace shortcuts
class Rot1(OneQubitGate):
    gate_name = 'Rot'
    gate_name_latex = r'\begin{array}{ll}\cos\theta&-\sin\theta\end{array}'
In [22]:
# This doesn't work:
#CircuitPlot(Rot1(0),1)
Can't stick a matrix in a qubit operator: ValueError: \begin{array}{ll}\cos\theta&-\sin\theta\end{array} ^ Unknown symbol: \begin (at char 0), (line:1, col:1) Doesn't work any better when I have $ at the beginning/end of the string.

Example 6

Goal:

In [23]:
U = CreateOneQubitGate('U')
CircuitPlot(Mz(0)*H(1)*CGate((0,1,3),U(2))*X(3)*CGateS((2,3),X(1)),4,labels=labeller(4,'j'))
Out[23]:
<sympy.physics.quantum.circuitplot.CircuitPlot at 0x104e68c90>
In [24]:
# Qasm version:
q = Qasm("def	c-U,3,'U'",
    "qubit	j0",
    "qubit	j1",
	"qubit	j2",
    "qubit	j3",
    "toffoli	j0,j1,j2",
    "X	j0",
    "c-U	j2,j3,j0,j1",
    "H	j2",
    "measure	j3")
q.plot()

Example 7

Goal:

In [25]:
V = CreateOneQubitGate('V')

CircuitPlot(CGate(1,V(0))*Mz(1)*H(1)*CGate(1,U(0))*H(1),2,labels=labeller(2))
Out[25]:
<sympy.physics.quantum.circuitplot.CircuitPlot at 0x10515b450>
In [26]:
# Qasm
q = Qasm("def	c-U,1,'U'","def	c-V,1,'V'",
         "qubit	q0","qubit	q1",
         "H	q0","c-U	q0,q1","H	q0",
         "measure	q0","c-V	q0,q1")
q.plot()

Example 8

Goal:

In [27]:
CircuitPlot(H(2)*H(2)*CGate(0,Z(2))*H(2)*CNOT(1,0)*CNOT(2,1)*CNOT(1,0)*CNOT(2,1)*H(1),3,labels=labeller(3))
Out[27]:
<sympy.physics.quantum.circuitplot.CircuitPlot at 0x105104c50>
In [28]:
#Qasm:
q = Qasm("def	c-Z,1,'Z'",
         "qubit	q0,\psi","qubit	q1,0","qubit	q2,0",
         "H	q1","cnot	q0,q1","cnot	q1,q2","cnot	q0,q1","cnot	q1,q2",
         "H	q0","c-Z	q2,q0","H	q0","H	q0")
q.plot()

Example 9

Goal:

In [29]:
SqrtX = CreateOneQubitGate('sqrt-X','\sqrt{X}')
SqrtXdag = CreateOneQubitGate('sqrt-X-dag','\sqrt{X}^\dagger')

CircuitPlot(CGate(2,SqrtX(0))*CNOT(2,1)*CGate(1,SqrtXdag(0))*CNOT(2,1)*CGate(1,SqrtX(0)),3,labels=labeller(3))
Out[29]:
<sympy.physics.quantum.circuitplot.CircuitPlot at 0x104f62a10>
In [30]:
q = Qasm("def	c-X,1,'\sqrt{X}'","def	c-Xd,1,'{\sqrt{X}}^\dagger'",
         "qubit	q0","qubit	q1","qubit	q2",
         "c-X	q1,q2","cnot	q0,q1","c-Xd	q1,q2",
         "cnot	q0,q1","c-X	q0,q2")
q.plot()

Example 10

Goal:

Example 11

Goal:

Example 12

Goal:

Example 13

Goal:

In [40]:
# Only works if you convert the defboxes to defs and remove qubit s2:
qasm_lines = """\
	def	CU,1,'U'
	def	CU2,1,'U^2'
	def	CU4,1,'U^4'
	def	c-S,1,'S'
	def	c-T,1,'T'

	qubit	j0,0	# QFT qubits
	qubit	j1,0
	qubit	j2,0
	qubit	s0	# U qubits

	h	j0	# equal superposition
	h	j1
	h	j2

	CU4	j0,s0	# controlled-U
	CU2	j1,s0
	CU	j2,s0

	h	j0	# QFT
	c-S	j0,j1
	h	j1
	nop	j0
	c-T	j0,j2
	c-S	j1,j2
	h	j2
	nop	j0
	nop	j0
	nop	j1
	
	measure	j0	# final measurement
	measure	j1	
	measure	j2"""

q = Qasm(*qasm_lines.splitlines())
q.plot()

Example 14

Goal:

Example 15

Goal:

In [31]:
CircuitPlot(Mz(1)*CPHASE(0,1),2,labels=['+','\psi'])
Out[31]:
<sympy.physics.quantum.circuitplot.CircuitPlot at 0x10544ba90>

Example 16

Goal:

In [32]:
CircuitPlot(Mz(0)*CNOT(1,0)*CPHASE(1,2),4,labels=['0','\phi','\psi','\psi'])
Out[32]:
<sympy.physics.quantum.circuitplot.CircuitPlot at 0x105607bd0>

Example 17

Goal:

In [33]:
CircuitPlot(Mz(2)*Mz(1)*CPHASE(1,2)*CPHASE(1,0)*CPHASE(3,2),4,labels=['\phi','+','+','\psi'])
Out[33]:
<sympy.physics.quantum.circuitplot.CircuitPlot at 0x1050f53d0>

Surface code

These are the X and Z checks from a surface code:

In [34]:
CircuitPlot(Mz(4)*H(4)*CNOT(4,0)*CNOT(4,1)*CNOT(4,2)*CNOT(4,3)*H(4),5,labels=['d_4','d_3','d_2','d_1','a_0'])
Out[34]:
<sympy.physics.quantum.circuitplot.CircuitPlot at 0x105701350>
In [35]:
CircuitPlot(Mz(4)*CNOT(0,4)*CNOT(1,4)*CNOT(2,4)*CNOT(3,4),5,labels=['d_4','d_3','d_2','d_1','a_0'])
Out[35]:
<sympy.physics.quantum.circuitplot.CircuitPlot at 0x104fc2b10>

Steane code

Make $|\bar{0}\rangle$, Fig 12 from Aliferis, Gottesman, and Preskill.

In [36]:
CircuitPlot(CNOT(6,0)*CNOT(5,4)*CNOT(3,2)*CNOT(5,1)*CNOT(3,0)*CNOT(6,2)*CNOT(6,4)*CNOT(3,0),
    7,labels=["0","0","0","+","0","+","+"])
Out[36]:
<sympy.physics.quantum.circuitplot.CircuitPlot at 0x105862d50>

Let's use the power of python to make this a little easier:

In [37]:
import operator
def prod(c): return reduce(operator.mul, c, 1)

steane_pairs = [(6,0),(5,4),(3,2),(5,1),(3,0),(6,2),(6,4),(3,0)]
circuit2 = prod([CNOT(i,j) for i,j in steane_pairs])
labels=["0","0","0","+","0","+","+"]
CircuitPlot(circuit2,7,labels=labels)
Out[37]:
<sympy.physics.quantum.circuitplot.CircuitPlot at 0x1059d0b50>

We can now extend this to a full Steane check:

In [38]:
circuit1 = prod([CNOT(i+7,j+7) for i,j in steane_pairs])
decoder = prod([CNOT(i+7,j+7) for i,j in reversed(steane_pairs)])
transverse = prod([CPHASE(i+7,i) for i in range(7)])
CircuitPlot(decoder*transverse*circuit2*circuit1,14,labels=labels+labels)
Out[38]:
<sympy.physics.quantum.circuitplot.CircuitPlot at 0x1059c6b90>

That's only 7 real lines of code to plot the encoding.