Chapter 1. Introduction

Whether you’re an expert in software engineering, computer graphics, data science, or just a curious computerphile, this book is designed to show how the power of quantum computing might be relevant to you, by actually allowing you to start using it.

To facilitate this, the following chapters do not contain thorough explanations of quantum physics (the laws underlying quantum computing) or even quantum information theory (how those laws determine our abilities to process information). Instead, they present working examples providing insight into the capabilities of this exciting new technology. Most importantly, the code we present for these examples can be tweaked and adapted. This allows you to learn from them in the most effective way possible: by getting hands-on. Along the way, core concepts are explained as they are used, and only insofar as they build an intuition for writing quantum programs.

Our humble hope is that interested readers might be able to wield these insights to apply and augment applications of quantum computing in fields that physicists may not even have heard of. Admittedly, hoping to help spark a quantum revolution isn’t that humble, but it’s definitely exciting to be a pioneer.

Required Background

The physics underlying quantum computing is full of dense mathematics. But then so is the physics behind the transistor, and yet learning C++ need not involve a single physics equation. In this book we take a similarly programmer-centric approach, circumventing any significant mathematical background. That said, here is a short list of knowledge that may be helpful in digesting the concepts we introduce:

  • Familiarity with programming control structures (if, while, etc.). JavaScript is used in this book to provide lightweight access to samples that can be run online. If you’re new to JavaScript but have some prior programming experience, the level of background you need could likely be picked up in less than an hour. For a more thorough introduction to JavaScript, see Learning JavaScript by Todd Brown (O’Reilly).

  • Some relevant programmer-level mathematics, necessitating:

    • An understanding of using mathematical functions

    • Familiarity with trigonometric functions

    • Comfort manipulating binary numbers and converting between binary and decimal representations

    • A comprehension of the basic meaning of complex numbers

  • A very elementary understanding of how to assess the computational complexity of an algorithm (i.e., big-o notation).

One part of the book that reaches beyond these requirements is Chapter 13, where we survey a number of applications of quantum computing to machine learning. Due to space constraints our survey gives only very cursory introductions to each machine-learning application before showing how a quantum computer can provide an advantage. Although we intend the content to be understandable to a general reader, those wishing to really experiment with these applications will benefit from a bit more of a machine-learning background.

This book is about programming (not building, nor researching) quantum computers, which is why we can do without advanced mathematics and quantum theory. However, for those interested in exploring the more academic literature on the topic, Chapter 14 provides some good initial references and links the concepts we introduce to mathematical notations commonly used by the quantum computing research community.

What Is a QPU?

Despite its ubiquity, the term “quantum computer” can be a bit misleading. It conjures images of an entirely new and alien kind of machine—one that supplants all existing computing software with a futuristic alternative.

At the time of writing this is a common, albeit huge, misconception. The promise of quantum computers stems not from them being a conventional computer killer, but rather from their ability to dramatically extend the kinds of problems that are tractable within computing. There are important computational problems that are easily calculable on a quantum computer, but that would quite literally be impossible on any conceivable standard computing device that we could ever hope to build.1

But crucially, these kinds of speedups have only been seen for certain problems (many of which we later elucidate on), and although it is anticipated that more will be discovered, it’s highly unlikely that it would ever make sense to run all computations on a quantum computer. For most of the tasks taking up your laptop’s clock cycles, a quantum computer performs no better.

In other words—from the programmer’s point of view—a quantum computer is really a co-processor. In the past, computers have used a wide variety of co-processors, each suited to their own specialties, such as floating-point arithmetic, signal processing, and real-time graphics. With this in mind, we will use the term QPU (Quantum Processing Unit) to refer to the device on which our code samples run. We feel this reinforces the important context within which quantum computing should be placed.

As with other co-processors such as the GPU (Graphics Processing Unit), programming for a QPU involves the programmer writing code that will primarily run on the CPU (Central Processing Unit) of a normal computer. The CPU issues the QPU co-processor commands only to initiate tasks suited to its capabilities.

A Hands-on Approach

Hands-on samples form the backbone of this book. But at the time of writing, a full-blown, general-purpose QPU does not exist—so how can you hope to ever run our code? Fortunately (and excitingly), even at the time of writing a few prototype QPUs are currently available, and can be accessed on the cloud. Furthermore, for smaller problems it’s possible to simulate the behavior of a QPU on conventional computing hardware. Although simulating larger QPU programs becomes impossible, for smaller code snippets it’s a convenient way to learn how to control an actual QPU. The code samples in this book are compatible with both of these scenarios, and will remain both usable and pedagogical even as more sophisticated QPUs become available.

There are many QPU simulators, libraries, and systems available. You can find a list of links to several well-supported systems at http://oreilly-qc.github.io. On that page, we provide the code samples from this book, whenever possible, in a variety of languages. However, to prevent code samples from overwhelming the text, we provide samples only in JavaScript for QCEngine. QCEngine is a free online quantum computation simulator, allowing users to run samples in a browser, with no software installation at all. This simulator was developed by the authors, initially for their own use and now as a companion for this book. QCEngine is especially useful for us, both because it can be run without the need to download any software and because it incorporates the circle notation that we use as a visualization tool throughout the book.

A QCEngine Primer

Since we’ll rely heavily on QCEngine, it’s worth spending a little time to see how to navigate the simulator, which you can find at http://oreilly-qc.github.io.

Running code

The QCEngine web interface, shown in Figure 1-1, allows you to easily produce the various visualizations that we’ll rely on. You can create these visualizations by simply entering code into the QCEngine code editor.

The QCEngine UI
Figure 1-1. The QCEngine UI

To run one of the code samples from the book, select it from the drop-down list at the top of the editor and click the Run Program button. Some new interactive UI elements will appear for visualizing the results of running your code (see Figure 1-2).

Quantum circuit visualizer

This element presents a visual representation of the circuit representing your code. We introduce the symbols used in these circuits in Chapters 2 and 3. This view can also be used to interactively step through the program (see Figure 1-2).

Circle-notation visualizer

This displays the so-called circle-notation visualization of the QPU (or simulator) register. We explain how to read and use this notation in Chapter 2.

QCEngine output console

This is where any text appears that may have been printed from within your code (i.e., for debugging) using the qc.print() command. Anything printed with the standard JavaScript console.log() function will still go to your web browser’s JavaScript console.

QCEngine UI elements for visualizing QPU results
Figure 1-2. QCEngine UI elements for visualizing QPU results

Debugging code

Debugging QPU programs can be tough. Quite often the easiest way to understand what a program is doing is to slowly step through it, inspecting the visualizations at each step. Hovering your mouse over the circuit visualizer, you should see a vertical orange line appear at a fixed position and a gray vertical line wherever in the circuit your cursor happens to be. The orange line shows which position in the circuit (and therefore the program) the circle-notation visualizer currently represents. By default this is the end of the program, but by clicking other parts of the circuit, you can have the circle-notation visualizer show the configuration of the QPU at that point in the program. For example, Figure 1-3 shows how the circle-notation visualizer changes as we switch between two different steps in the default QCEngine program.

Stepping through a QCEngine program using the circuit and circle-notation visulizers
Figure 1-3. Stepping through a QCEngine program using the circuit and circle-notation visualizers

Having access to a QPU simulator, you’re probably keen to start tinkering. Don’t let us stop you! In Chapter 2 we’ll walk through code for increasingly complex QPU programs.

Native QPU Instructions

QCEngine is one of several tools allowing us to run and inspect QPU code, but what does QPU code actually look like? Conventional high-level languages are commonly used to control lower-level QPU instructions (as we’ve already seen with the JavaScript-based QCEngine). In this book we’ll regularly cross between these levels. Describing the programming of a QPU with distinctly quantum machine-level operations helps us get to grips with the fundamental novel logic of a QPU, while seeing how to manipulate these operations from higher-level conventional languages like JavaScript, Python, or C++ gives us a more pragmatic paradigm for actually writing code. The definition of new, bespoke, quantum programming languages is an active area of development. We won’t highlight these in this book, but references for the interested reader are offered in Chapter 14.

To whet your appetite, we list some of the fundamental QPU instructions in Table 1-1, each of which will be explained in more detail within the chapters ahead.

Table 1-1. Essential QPU instruction set
Symbol Name Usage Description
prqc 01in01

NOT (also X)

qc.not(t)

Logical bitwise NOT

prqc 01in02

CNOT

qc.cnot(t,c)

Controlled NOT: if (c) then NOT(t)

prqc 01in03

CCNOT (Toffoli)

qc.cnot(t,c1|c2)

if (c1 AND c2) then NOT(t)

prqc 01in04

HAD (Hadamard)

qc.had(t)

Hadamard gate

prqc 01in05

PHASE

qc.phase(angle,c)

Relative phase rotation

prqc 01in06

Z

qc.phase(180,c)

Relative phase rotation by 180 °

prqc 01in07

S

qc.phase(90,c)

Relative phase rotation by 90 °

prqc 01in08

T

qc.phase(45,c)

Relative phase rotation by 45 °

prqc 01in09

CPHASE

qc.cphase(angle,c1|c2)

Conditional phase rotation

prqc 01in10

CZ

qc.cphase(180,c1|c2)

Conditional phase rotation by 180 °

prqc 01in11

READ

val = qc.read(t)

Read qubits, returning digital data

prqc 01in12

WRITE

qc.write(t,val)

Write conventional data to qubits

prqc 01in13

ROOTNOT

qc.rootnot(t)

Root-of-NOT operation

prqc 01in14

SWAP (EXCHANGE)

qc.exchange(t1|t2)

Exchange two qubits

prqc 01in15

CSWAP

qc.exchange(t1|t2, c)

Conditional exchange: if (c) then SWAP(t1,t2)

With each of these operations, the specific instructions and timing will depend on the QPU brand and architecture. However, this is an essential set of basic operations expected to be available on all machines, and these operations form the basis of our QPU programming, just as instructions like MOV and ADD do for CPU programmers.

Simulator Limitations

Although simulators offer a fantastic opportunity to prototype small QPU programs, when compared to real QPUs they are hopelessly underpowered. One measure of the power of a QPU is the number of qubits it has available to operate on2 (the quantum equivalent of bits, on which we’ll have much more to say shortly).

At the time of this book’s publication, the world record for the largest simulation of a QPU stands at 51 qubits. In practice, the simulators and hardware available to most readers of this book will typically be able to handle 26 or so qubits before grinding to a halt.

The examples in this book have been written with these limitations in mind. They make a great starting point, but each qubit added to them will double the memory required to run the simulation, cutting its speed in half.

Hardware Limitations

Conversely, the largest actual QPU hardware available at the time of writing has around 70 physical qubits, while the largest QPU available to the public, through the Qiskit open source software development kit, contains 16 physical qubits.3 By physical, as opposed to logical, we mean that these 70 qubits have no error correction, making them noisy and unstable. Qubits are much more fragile than their conventional counterparts; the slightest interaction with their surroundings can derail the computation.

Dealing with logical qubits allows a programmer to be agnostic about the QPU hardware and implement any textbook algorithm without having to worry about specific hardware constraints. In this book, we focus solely on programming with logical qubits, and while the examples we provide are small enough to be run on smaller QPUs (such as the ones available at the time of publication), abstracting away physical hardware details means that the skills and intuitions you develop will remain invaluable as future hardware develops.

QPU Versus GPU: Some Common Characteristics

The idea of programming an entirely new kind of processor can be intimidating, even if it does already have its own Stack Exchange community. Here’s a list of pertinent facts about what it’s like to program a QPU:

  • It is very rare that a program will run entirely on a QPU. Usually, a program running on a CPU will issue QPU instructions, and later retrieve the results.

  • Some tasks are very well suited to the QPU, and others are not.

  • The QPU runs on a separate clock from the CPU, and usually has its own dedicated hardware interfaces to external devices (such as optical outputs).

  • A typical QPU has its own special RAM, which the CPU cannot efficiently access.

  • A simple QPU will be one chip accessed by a laptop, or even perhaps eventually an area within another chip. A more advanced QPU is a large and expensive add-on, and always requires special cooling.

  • Early QPUs, even simple ones, are the size of refrigerators and require special high-amperage power outlets.

  • When a computation is done, a projection of the result is returned to the CPU, and most of the QPU’s internal working data is discarded.

  • QPU debugging can be very tricky, requiring special tools and techniques. Stepping through a program can be difficult, and often the best approach is to make changes to the program and observe their effect on the output.

  • Optimizations that speed up one QPU may slow down another.

Sounds pretty challenging. But here’s the thing—you can replace QPU with GPU in each and every one of those statements and they’re still entirely valid.

Although QPUs are an almost alien technology of incredible power, when it comes to the problems we might face in learning to program them, a generation of software engineers have seen it all before. It’s true, of course, that there are some nuances to QPU programming that are genuinely novel (this book wouldn’t be necessary otherwise!), but the uncanny number of similarities should be reassuring. We can do this!

1 One of our favorite ways to drive this point home is the following back-of-the-envelope calculation. Suppose conventional transistors could be made atom-sized, and we aimed to build a warehouse-sized conventional computer able to match even a modest quantum computer’s ability to factor prime integers. We would need to pack transistors so densely that we would create a gravitational singularity. Gravitational singularities make computing (and existing) very hard.

2 Despite its popularity in the media as a benchmark for quantum computing, counting the number of qubits that a piece of hardware can handle is really an oversimplification, and much subtler considerations are necessary to assess a QPU’s true power.

3 This figure may become out of date while this book is in press!

Get Programming Quantum Computers now with O’Reilly online learning.

O’Reilly members experience live online training, plus books, videos, and digital content from 200+ publishers.