Quil For Python Programmers
Part 1: Intro to Quantum and Quil
What is Quil?
Quil stands for Quantum Instruction Language and is an instruction set for quantum computers. Think of it as like Assembly, but for quantum computers! Now, you may be wondering why I'm going to make this tutorial for Python programmers and not Assembly programmers. Well, quantum computing is largely in the fields involving computation, not really so much just having low-level control of a device, so most Python programmers would find greater use of Quil and overall quantum technology than most x86/64 programmers would.
Now, Quil isn't the only of its type. There are also other quantum languages, the most notable of which are QASM and Qiskit (made by IBM). However, Quil is the only one on Repl.it currently. You can make a Quil repl here.
Quantum Physics Basics
So, quantum computing highly leverages quantum mechanics. Though, we just need to really talk about superposition. Superposition is the idea that a quantum particle can exist in multiple states at one, with different probabilities of each state. The superposition, however, is lost when you measure it and then it becomes an actual value. Quantum computing uses these probabilities rather than the values in pretty much all cases other than conditional statements, although even this may change in the not so distant future!
Quil Basics - Defining a Gate
This part of the tutorial largely points to https://repl.it/talk/share/Quantum-RNG/60110 which also happens to be the first Quil Repl.it post ever if you would like to check it out!
So, in quantum computing, quantum gates are defined by matrices. I'm not personally too familiar with linear algebra, so I'll have to explain this in a later tutorial. However, the Hadamard gate, a gate that produces a 50-50 random decision, has a matrix of
[[sqrt(2)/2, sqrt(2)/2], [sqrt(2)/2, -sqrt(2)/2]]. Now, many gates are predefined in Quil, but you can also define your own! Let's look at how to define the Hadamard gate.
DEFGATE HADAMARD: sqrt(2)/2, sqrt(2)/2 sqrt(2)/2, -sqrt(2)/2
Note: You have to double-indent or it won't work.
So, as you can see here, we have the
DEFINE keyword which says we are defining a gate. Then, we have the name of the gate,
HADAMARD, and a
: to show we are starting the matrix definition. Then, we include the matrix for the Hadamard gate. Now, you can call this by using
HADAMARD <qubit>, for example,
HADAMARD 1 which will apply the Hadamard gate to qubit 1. However, the Hadamard gate is also built in, so you could just use
H 1 instead as
H is the built-in version.
Quil Basics - Random Number Generator
Now, we will talk about how to create a random number generator in Quil!
First off, you can redefine the Hadamard gate if you would like or you can just use
Then, you need to apply the Hadamard gate to all 4 qubits (we are doing a random number 0-15) like this:
H 0 H 1 H 2 H 3
Now, this puts all 4 qubits in a superposition between 0 and 1. Now, we need to measure the superposition. For this, we use the
MEASURE 0  MEASURE 1  MEASURE 2  MEASURE 3 
This takes the qubits out of superposition so a number can be displayed. However, note the
 for example. That means that you are putting the value into classical bit 0. Of course, Quil will return the qubits, not the classical bits, but this is good to know, especially for conditionals as we will get to later on.
Now, this will print a number 0000 to 1111 or 0 to 15. It is in binary and there is no "print" function or anything in Quil as it is just for computation currently. However, real quantum processors can aid computers in high-powered computations, although we don't have an actual quantum computer yet.
Quil Logic and Control
Now, you can't create a good program without logic and control. If you just have random numbers, you just have a random number generator and can't do anything with it. Also, this is where the Python comes in!
First off, we need to talk about a few new functions:
The quantum X gate simply flips a qubit. So, if it is 1, you get 0 or if it is 0, you get 1. This can be represented with the following Python code. I'll also include the translation for the Hadamard gate and some basic set-up stuff...
from random import randint as r # We need to use the random module. qubits = [0, 0, 0] # Define 3 qubits for our program bits = [0, 0, 0, 0, 0] # Define 5 bits for our program # Hadamard gate def H(n): qubits[n] = r(0, 1) # Random 0 to 1 def X(n): qubits[n] = 1 - qubits[n]
SWAP command simply swaps two qubits! This can be represented in Python as
def SWAP(a, b): temp = qubits[a] qubits[a] = qubits[b] qubits[b] = temp
CNOT function is a conditional form of the
X gate. It is also in some instruction sets referred to as
CX. If the first qubit is 1, it will invert the second qubit. For example,
CNOT 0 1 will flip qubit 1 if qubit 0 is 1. We do not need a Python equivalent since we will not use that in our program.
CCNOT function is a conditional that only inverts a qubit if two conditions are true. It is a lot like a classical
AND gate. We can represent this as
def CCNOT(a, b, c): if qubits[a] == 1 and qubits[b] == 1: X(c)
Now, we also need to define
MEASURE and add a way to read a bit as a boolean, so we add this:
def MEASURE(a, b): bits[b] = qubits[a] def bit(n): return bits[n] == 1
Changing the Probabilities
Using these new gates and functions, we will create another random number generator, but make it instead a 25% chance of a 1 and 75% chance of a 0.
First, we know that
P(A and B) = 25% if
P(A) = 50% and
P(B) = 50%. We know that the Hadamard gate satisfies this, so we will make it return 1 if two Hadamard gates are true. First, we need to create these Hadamard gates.
H 0 H 1
Now, if both of these are 1, we want qubit 2 to be 1.
CCNOT 0 1 2
And we want to move qubit 2 to 0 so that we get 1, not 4, for the 25% chance.
SWAP 0 2
Now, we just need to get qubits 1 and 2 to 0 and measure.
In many versions of Quil, there is a
RESET function where you can reset a qubit to 0. However, Repl.it doesn't have that, so we have to make it ourselves!
MEASURE 1  JUMP-WHEN @one  X 1 LABEL @one X 1
First, this measures qubit 1 and puts it into bit 3. Then, if 3 is true, it will jump to the label
@one and only undergo 1
X gate. However, if it is not 1, it will do two
X gates and these will cancel each other out. This guarantees that qubit 1 will be 0 afterwards.
We can also do the same with qubit 2!
MEASURE 2  JUMP-WHEN @two  X 2 LABEL @two X 2
And lastly, we have to measure qubit 0.
MEASURE 0 
This will produce either
(1+0j)|001>. This means, that there is a 100% probability of 0 or 1 respectively.
The Final Programs
H 0 H 1 CCNOT 0 1 2 SWAP 0 2 MEASURE 1  JUMP-WHEN @one  X 1 LABEL @one X 1 MEASURE 2  JUMP-WHEN @two  X 2 LABEL @two X 2 MEASURE 0 
from random import randint as r qubits = [0, 0, 0] bits = [0, 0, 0, 0, 0] def H(n): qubits[n] = r(0, 1) def SWAP(a, b): temp = qubits[a] qubits[a] = qubits[b] qubits[b] = temp def X(n): qubits[n] = 1 - qubits[n] def CCNOT(a, b, c): if qubits[a] == 1 and qubits[b] == 1: X(c) def MEASURE(a, b): bits[b] = qubits[a] def bit(n): return bits[n] == 1 H(0) H(1) CCNOT(0, 1, 2) SWAP(0, 2) MEASURE(1, 3) if not bit(3): X(1) X(1) MEASURE(2, 4) if not bit(4): X(2) X(2) MEASURE(0, 0) qubits.reverse() print("".join(str(x) for x in qubits))
Thank you for reading this tutorial! If you have any questions, please leave a comment. Also, @amasad, would it be possible to update Quil on Repl.it and/or add QASM? Repl.it Quil is very stripped down and doesn't have many features, as we can even see with the lack of the