Ciro Santilli
🔗

# Programmer's model of quantum computers

| 🗗 split | ↑ parent "Quantum computing" | 1k, 3k, 5
🔗
This is a quick tutorial on how a quantum computer programmer thinks about how a quantum computer works. If you know:
a concrete and precise hello world operation can be understood in 30 minutes.
🔗
Although there are several types of quantum computer under development, there exists a single high level model that represents what any of those computers can do, and we are going to explain that model here.
🔗
Beyond that basic model. programmers only may have to consider the imperfections of their hardware, but the starting point will almost always be this basic model, and tooling that automates mapping the high level model to real hardware considering those imperfections (i.e. compilers) is already getting better and better.
🔗
This model is very simple to understand, being only marginally more complex than that of a classical computer, see also: https://quantumcomputing.stackexchange.com/questions/6639/is-my-background-sufficient-to-start-quantum-computing/14317#14317
🔗
The situation of quantum computers today in the 2020's is somewhat analogous to that of the early days of classical circuits and computers in the 1950's and 1960's, before CPUS came along and software ate the world. Even though the exact physics of a classical computer might be hard to understand and vary across different types of integrated circuits, those early hardware pioneers (and to this day modern CPU designers), can usefully view circuits from a higher level point of view, thinking only about concepts such as:
• logic gates like AND, NOR and NOT
• a clock + registers
as modelled at the register transfer level, and only in a separate compilation step translated into actual chips. This high level understanding of how a classical computer works is what we can call "the programmer's model of a classical computer". So we are now going to describe the quantum analogue of it.
🔗
The way quantum programmers think about a quantum computer in order to program can be described as follows:
• the input of a N qubit quantum computer is a vector of dimension N containing classic bits 0 and 1
• the quantum program, also known as circuit, is a unitary matrix of complex numbers that operates on the input to generate the output
• the output of a N qubit computer is also a vector of dimension N containing classic bits 0 and 1
🔗
To operate a quantum computer, you:
• set the classic input bits
• press a "RUN" button
• read the classic output bits
🔗
Each time you do this, you are literally conducting a physical experiment of the specific physical implementation of the computer:
• setup your physical system to represent the classical 0/1 inputs
• let the state evolve for long enough
• measure the classical output back out
and each run as the above can is simply called "an experiment" or "a measurement".
🔗
The output comes out "instantly" in the sense that it is physically impossible to observe any intermediate state of the system, i.e. there are no clocks like in classical computers, further discussion at: philosophical discussion of the programmer's model of quantum computers. Setting up, running the experiment and taking the does take some time however, and this is important because you have to run the same experiment multiple times because results are probabilistic as mentioned below.
🔗
Unlike in a classical computer, the output of a quantum computer is not deterministic however.
🔗
But the each output is not equally likely either, otherwise the computer would be useless excecpt as random number generator!
🔗
This is because the probabilities of each output for a given input depends on the program (unitary matrix) it went through.
🔗
Therefore, what we have to do is to design the quantum circuit in a way that the right or better answers will come out more likely than the bad answers.
🔗
We then calculate the error bound for our circuit based on its design, and then determine how many times we have to run the experiment to reach the desired accuracy.
🔗
The probability of each output of a quantum computer is derived from the input and the circuit as follows.
🔗
First we take the classic input vector of dimension N of 0's and 1's and convert it to a "quantum state vector" of dimension : $$qin​​∈C2n (1)$$
🔗
We are after all going to multiply it by the program matrix, as you would expect, and that has dimension !
🔗
Note that this initial transformation also transforms the discrete zeroes and ones into complex numbers.
🔗
For example, in a 3 qubit computer, the quantum state vector has dimension and the following shows all 8 possible conversions from the classic input to the quantum state vector:
000 -> 1000 0000 == (1.0, 0.0, 0.0, 0.0,  0.0, 0.0, 0.0, 0.0)
001 -> 0100 0000 == (0.0, 1.0, 0.0, 0.0,  0.0, 0.0, 0.0, 0.0)
010 -> 0010 0000 == (0.0, 0.0, 1.0, 0.0,  0.0, 0.0, 0.0, 0.0)
011 -> 0001 0000 == (0.0, 0.0, 0.0, 1.0,  0.0, 0.0, 0.0, 0.0)
100 -> 0000 1000 == (0.0, 0.0, 0.0, 0.0,  1.0, 0.0, 0.0, 0.0)
101 -> 0000 0100 == (0.0, 0.0, 0.0, 0.0,  0.0, 1.0, 0.0, 0.0)
110 -> 0000 0010 == (0.0, 0.0, 0.0, 0.0,  0.0, 0.0, 1.0, 0.0)
111 -> 0000 0001 == (0.0, 0.0, 0.0, 0.0,  0.0, 0.0, 0.0, 1.0)

🔗
This can be intuitively interpreted as:
• if the classic input is 000, then we are certain that all three bits are 0.
Therefore, the probability of all three 0's is 1.0, and all other possible combinations have 0 probability.
• if the classic input is 001, then we are certain that bit one and two are 0, and bit three is 1. The probability of that is 1.0, and all others are zero.
• and so on
🔗
Now that we finally have our quantum state vector, we just multiply it by the unitary matrix of the quantum circuit, and obtain the dimensional output quantum state vector : $$qout​​=Qqin​​ (2)$$
🔗
And at long last, the probability of each classical outcome of the measurement is proportional to the square of the length of each entry in the quantum vector, analogously to what is done in the Schrodinger equation.
🔗
For example, suppose that the 3 qubit output were: $$qout​​​=⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎡​23​​0.021​0.00.00.00.00.0​⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎤​​ (3)$$
🔗
Then, the probability of the first and third possible outcomes would be the length of each component squared: $$∣∣∣∣∣∣​23​​∣∣∣∣∣∣​2∣∣∣∣∣​21​2∣∣∣∣∣​2​=23​​2=21​2​=43​=41​​ (4)$$ i.e. 75% for the first, and 25% for the third outcomes, where just like for the input:
• first outcome means 000: all output bits are zero
• third outcome means 010: the first and third bits are zero, but the second one is 1
🔗
All other outcomes have probability 0 and cannot occur, e.g.: 001 is impossible.
🔗
Keep in mind that the quantum state vector can also contain complex numbers because we are doing quantum mechanics, but we just take their magnitude in that case, e.g. the following quantum state would lead to the same probabilities as the previous one: $$∣∣∣∣∣∣​21+2​i​∣∣∣∣∣∣​2∣∣∣∣∣​2i​∣∣∣∣∣​2​=2212+22​​=2212​​=43​=41​​ (5)$$
🔗
This interpretation of the quantum state vector clarifies a few things:
• the input quantum state is just a simple state where we are certain of the value of each classic input bit
• the matrix has to be unitary because the total probability of all possible outcomes must be 1.0
This is true for the input matrix, and unitary matrices have the probability of maintaining that property after multiplication.
Unitary matrices are a bit analogous to self-adjoint operators in general quantum mechanics (self-adjoint in finite dimensions implies is stronger)
This also allows us to understand intuitively why quantum computers may be capable of accelerating certain algorithms exponentially: that is because the quantum computer is able to quickly do an unitary matrix multiplication of a humongous sized matrix.
If we are able to encode our algorithm in that matrix multiplication, considering the probabilistic interpretation of the output, then we stand a chance of getting that speedup.
🔗

## 1. Quantum logic gate

🔗
At programmer's model of quantum computers we saw that making a quantum circuit actually comes down to designing one big unitary matrix.
🔗
We have to say though that that was a bit of a lie.
🔗
Quantum programmers normally don't just produce those big matrices manually from scratch.
🔗
Instead, they use quantum logic gates.
🔗
There are a few reasons for that depending on how you look at it, these are described at:
🔗

### 1.1. Why quantum logic gates are needed: physical implementation

🔗
One direct practical reason is that we need to map the matrix to real quantum hardware somehow, and all quantum hardware designs so far and likely in the future are gate-based: you manipulate a small number of qubits at a time (2) and add more and more of such operations.
🔗
While there are "quantum compilers" to increase the portability of quantum programs, it is to be expected that programs manually crafted for a specific hardware will be more efficient just like in classic computers.
🔗
TODO: is there any clear reason why computers can't beat humans in approximating any unitary matrix with a gate set?
🔗
This is analogous to what classic circuit programmers will do, by using smaller logic gates to create complex circuits, rather than directly creating one huge truth table.
🔗
The most commonly considered quantum gates take 1, 2, or 3 qubits as input.
🔗
The gates themselves are just unitary matrices that operate on the input qubits and produce the same number of output qubits.
🔗
For example, the matrix for the quantum CNOT gate, which takes 2 qubits as input is:
🔗
1 0 0 0
0 1 0 0
0 0 0 1
0 0 1 0

🔗
The final question is then: if I have a 2 qubit gate but an input with more qubits, say 3 qubits, then what does the 2 qubit gate (4x4 matrix) do for the final big 3 qubit matrix (8x8)? In order words, how do we scale quantum gates up to match the total number of qubits?
🔗
The intuitive answer is simple: we "just" extend the small matrix with a larger identity matrix so that the sum of the probabilities third bit is unaffected.
🔗
More precisely, we likely have to extend the matrix in a way such that the partial measurement of the original small gate qubits leaves all other qubits unaffected.
🔗
For example, if the circuit were made up of a CNOT gate operating on the first and second qubits as in:
0 ----+----- 0
|
1 ---CNOT--- 1

2 ---------- 2

🔗
then we would just extend the 2x2 CNOT gate to:
🔗
TODO lazy to properly learn right now. Apparently you have to use the Kronecker product by the identity matrix.
🔗

### 1.2. Why quantum logic gates are needed: it is not possible to compute the matrix explicitly

🔗
One key insight, is that the matrix of a non-trivial quantum circuit is going to be huge, and won't fit into any amount classical memory that can be present in this universe.
🔗
This is because the matrix is exponential in the number qubits, and is more than the number of atoms in the universe!
🔗
Therefore, off the bat we know that we cannot possibly describe those matrices in an explicit form, but rather must use some kind of shorthand.
🔗
But it gets worse.
🔗
Even if we had enough memory, the act of explicitly computing the matrix is not generally possible.
🔗
This is because knowing the matrix, basically means knowing the probability result for all possible outputs for each of the possible inputs.
🔗
But if we had those probabilities, our algorithmic problem would already be solved in the first place! We would "just" go over each of those output probabilities (OK, there are of those, which is also an insurmountable problem in itself), and the largest probability would be the answer.
🔗
So if we could calculate those probabilities on a classical machine, we would also be able to simulate the quantum computer on the classical machine, and quantum computing would not be able to give exponential speedups, which we know it does.
🔗
To see this, consider that for a given input, say 000 on a 3 qubit machine, the corresponding 8-sized quantum state looks like:
🔗
000 -> 1000 0000 == (1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)

🔗
and therefore when you multiply it by the unitary matrix of the quantum circuit, what you get is the first column of the unitary matrix of the quantum circuit. And 001, gives the second column and so on.
🔗
As a result, to prove that a quantum algorithm is correct, we need to be a bit smarter than "just calculate the full matrix".
🔗
Which is why you should now go and read: Section "Quantum algorithm".
🔗
This type of thinking links back to how physical experiments relate to quantum computing: a quantum computer realizes a physical experiment to which we cannot calculate the probabilities of outcomes without exponential time.
🔗
So for example in the case of a photonic quantum computer, you are not able to calculate from theory the probability that photons will show up on certain wires or not.
🔗

### 1.3. Universal quantum gates

🔗
Just like as for classic gates, we would like to be able to select quantum computer physical implementations that can represent one or a few gates that can be used to create any quantum circuit.
🔗
Unfortunately, in the case of quantum circuits this is obviously impossible, since the space of N x N unitary matrices is infinite and continuous.
🔗
Therefore, when we say that certain gates form a "set of universal quantum gates", we actually mean that "any unitary matrix can be approximated to arbitrary precision with enough of these gates".
🔗
Or if you like fancy Mathy words, you can say that the subgroup of the unitary group generated by our basic gate set is a dense subset of the unitary group.
🔗

## 2. Philosophical discussion of the programmer's model of quantum computers

🔗
Just like a classic programmer does not need to understand the intricacies of how transistors are implemented and CMOS semiconductors, the quantum programmer does not understand physical intricacies of the underlying physical implementation.
🔗
The main difference to keep in mind is that quantum computers cannot save and observe intermediate quantum state, so programming a quantum computer is basically like programming a combinatorial-like circuit with gates that operate on (qu)bits:
🔗
For this reason programming a quantum computer is much like programming a classical combinatorial circuit as you would do with SPICE, verilog-or-vhdl, in which you are basically describing a graph of gates that goes from the input to the output
🔗
For this reason, we can use the words "program" and "circuit" interchangeably to refer to a quantum program
🔗
Also remember that and there is no no clocks in combinatorial circuits because there are no registers to drive; and so there is no analogue of clock in the quantum system either,
🔗
Another consequence of this is that programming quantum computers does not look like programming the more "common" procedural programming languages such as C or Python, since those fundamentally rely on processor register / memory state all the time.
🔗
Quantum programmers can however use classic languages to help describe their quantum programs more easily, for example this is what happens in Qiskit, where you write a Python program that makes Qiskit library calls that describe the quantum program.
🔗

🔗