I’ve been thinking alot about quantum computing recently. As "Hello, World!" is usually a great place to start with any computing task, I thought I’d take a stab at the QC equivalent.

Since most useful quantum circuits require understanding some pretty dense math, I like James Wootton’s post Making a quantum computer smile for its playful simplicity. It’ll add a lot of context here, so go ahead and read it now. I’ll wait ;)

What follows is essentially a re-implementation of that quantum circuit, with two main differences:

  1. I’ll use both IBM Q Experience (as Wootton did) as well as Rigetti Forest. This is mostly to get a feel for the development experience of both and see which one I would like to spend more time with.

  2. I’ll focus on hand-writing circuits in QASM and QUIL instead of building circuits using QISKit or pyQuil respectively. My idea here is to focus more on the fundamentals of quantum computing than the particulars of the APIs currently wrapping them. QASM and QUIL are pretty similar (as we’ll see), but QISKit and pyQuil have different APIs despite both being python bindings for describing quantum ciruits. Also, there are many tutorials and docs for these SDKs, but very few for the cirtuit specification languages.

Connecting to the QVM

To get the python SDK code out of the way, we want SDK wrapper code for QISKit and pyQuil that will each:

  • take as input a filename (of a circuit specification file)
  • load this file and parse it
  • use API credentials to execute the circuit on a remote 16qubit quantum virtual machine
  • return the bitstring distribution of 100 runs of this cirtuit

Not super important to understand this code. I tried to highlight similarities over differences here, so the following code will most likely trigger configuration and deprecation warnings:

Wrapper for QASM:

from qiskit import QuantumProgram

QISKIT_API_KEY = 'your_key'

def qasm(filename):
    program = QuantumProgram()
    program.set_api(
        QISKIT_API_KEY,
        'https://quantumexperience.ng.bluemix.net/api', # default
    )

    with open(filename) as fd:
        program.load_qasm_text(fd.read())

    result = program.execute(backend='ibmqx_qasm_simulator', shots=100)
    return result.get_data()['counts']

Wrapper for QUIL:

from collections import Counter

from pyquil.api import QVMConnection
from pyquil.parser import parse_program

PYQUIL_API_KEY = 'your_key'
PYQUIL_USER_ID = 'your_id'

def quil(filename):
    qvm = QVMConnection(
        api_key       = PYQUIL_API_KEY,
        user_id       = PYQUIL_USER_ID,
        sync_endpoint = 'https://api.rigetti.com', # default
    )

    with open(filename) as fd:
        program = parse_program(fd.read())

    result = qvm.run(program, range(16), trials=100)
    return Counter(''.join(str(bit) for bit in measurement) for measurement in result)

So far not bad! Now just the main wrapper and some helper functions:


def main():
    print('Running QASM:')
    output(qasm('supersmile.qasm'))
    print('Running QUIL:')
    output(quil('supersmile.quil'))

def bits_to_str(bits):
    '''
        >>> bits_to_str('0011101100101001')
        ;)
    '''
    num = int(''.join(bits), 2)
    return num.to_bytes(len(bits) // 8, 'big').decode()

def output(summary):
    '''
        >>> output({'0011101100101001': 100})
        100% ;)
    '''
    for bits, percent in summary.items():
        print('{}% {}'.format(percent, bits_to_str(bits)))
 
if __name__ == '__main__':
    main()

Initialization

Next we need to write the actual circuits! Starting with making sure we have 16 qubits to create the smileys.

QASM:

OPENQASM 2.0;
include "qelib1.inc";

// init 16 qubits to 0
qreg q[16];
creg c[16];

Here I’m using standard gates with 16 qubits and 16 classical bits.

The corresponding QUIL:


PRAGMA comment "init 16 qubits to 0"
I 0
I 1
I 2
I 3
I 4
I 5
I 6
I 7
I 8
I 9
I 10
I 11
I 12
I 13
I 14
I 15

Notes so far:

  • I’m hijacking quil’s pragmas to produce comments.
  • theoretically the quil initializations are uneccesary, but in practice the program doesn’t work without them. And hey, Explicit is better than implicit.
  • quil’s 16 classical bits are implicit :/
  • quil’s spec claims to need INCLUDE "stdgates.quil", but in practice doesn’t need it, and execution actually breaks if you add that line

The Actual Circuit

Remember that the goal is to create a superposition of :

;) = 00111011 00101001
8) = 00111000 00101001

So our algorithm is:

  • flip bits 2-4
  • superposition “00” and “11” in bits 6-7
  • flip bits 10,12, and 15

QASM:

// setup smiley superposition
x q[2];
x q[3];
x q[4];
h q[6];
cx q[6],q[7];
x q[10];
x q[12];
x q[15];

QUIL:

PRAGMA comment "setup smiley superposition"
X 2
X 3
X 4
H 6
CNOT 6 7
X 10
X 12
X 15

Again, super similar.

Measurement

Since this will run on a QVM, we could therefore get the waveform directly. But observables and measurement is a core part of quantum computing, and incorporating them will allow execution on a QPU as well. Anyone out there have a quantum computer I can borrow access to? ;)

QASM:

// measure 16 qubits into reversed classical bits
measure q[15] -> c[0];
measure q[14] -> c[1];
measure q[13] -> c[2];
measure q[12] -> c[3];
measure q[11] -> c[4];
measure q[10] -> c[5];
measure q[9]  -> c[6];
measure q[8]  -> c[7];
measure q[7]  -> c[8];
measure q[6]  -> c[9];
measure q[5]  -> c[10];
measure q[4]  -> c[11];
measure q[3]  -> c[12];
measure q[2]  -> c[13];
measure q[1]  -> c[14];
measure q[0]  -> c[15];

QUIL:

PRAGMA comment "measure 16 qubits into classical bits" 
MEASURE 15 [15]
MEASURE 14 [14]
MEASURE 13 [13]
MEASURE 12 [12]
MEASURE 11 [11]
MEASURE 10 [10]
MEASURE 9  [9]
MEASURE 8  [8]
MEASURE 7  [7]
MEASURE 6  [6]
MEASURE 5  [5]
MEASURE 4  [4]
MEASURE 3  [3]
MEASURE 2  [2]
MEASURE 1  [1]
MEASURE 0  [0]

The only real difference here is that quil’s notation gives qubit 0 the least significance, and thus we don’t need to reverse the ordering.

Conclusions

QASM and QUIL are pretty similar! And very different from say… Q#. Hopefully this means that it won’t be long before we see higher level languages that compile to targets or either QASM or QUIL.

Oh, and the actual results of execution?

taylor@taylor:~$ python3 supersmile.py 
Running QASM:
46% ;)
54% 8)
Running QUIL:
45% ;)
55% 8)

Success!

Followup posts may include:

  • incorporating different noise models
  • creating superpositions of musical notes instead of smileys
  • actually useful algorithm implementations