Quantum Machine Learning For Babies (Part 2)

Eya Abid
8 min readOct 26, 2022

1- It is as easy as you want it to be…

Writing the first part, I had an inner question bugging my head: is this too easy for a quantum article?

This question has gotten bigger and bigger whenever I get a comment about how simple I made things breaking down the knowledge.

The thing is, that is my method of learning/teaching: inductive learning.

If a piece of information is too complex to be digested, break it down into smaller, more significant pieces until the big picture builds itself and scales itself up into a clearer, more complex picture. And trust me, that is the correct way of learning because throwing the information into the brain block by block does not respect the brain architecture. Sadly enough, this is the main ‘pedagogical’ method used by most teachers (at least in my scope of vision).

Nevertheless, I learned things the hard way. I had to gasp the information from the complicated books and articles I found online, all of which required some initial training, and at least a Ph.D. in quantum mechanics. The good news is, you don’t have to digest this content via this portal. You could follow up with the baby steps I am instructing in this series, and everything would come up together eventually.

In this part, we will explore the qubit, that mysterious quantum bit, while writing down our very first quantum circuit which is a series of quantum bit transformations (a.k.a the quantum program).

As far as we know, every transformation is determined by a finite number of states. Things are not that different on a quantum level. We will start by exploring these quantum states at their origin.

2- Decoding the quantum states

In the previous part, we discussed the superposition principle in quantum computing, which states that the system is in different states concurrently unless you measure it.

We will take the example of a spin of a particle. This spin is not up or down, yet it is up and down simultaneously. However, when you decide to look at it, you find it either up or down.

Or, let’s say that you have a quantum coin, and you decided to flip it to see how it behaves. In the air, it has both values, heads, and tails. If and only if you catch it and look at it, it decides for a value. Once landed, it is a normal coin with heads up or tails up.

This might look slightly similar to a normal behavior system, where you actually can not determine the actual state of the coin unless it lands. Bare in mind that the conditions are different in a quantum state since this system does NOT depend on initial conditions, making the probabilities of getting heads or tails really, really random.

In classical mechanics, there is no randomness. Things may “look” random, but they are not, and if measured with infinite precision, randomness would fade away.

Maybe you’re pondering: Okay what is the huge fuss about this randomness?

The big thing is the consequences. In a classic system, a system sensitive dependent to initial conditions, the answer to a question is already determined before we ask it.

Rather than watching the soccer match tonight, you spend the evening with your friends. When you return home, even though you don’t know the results, the match is over, and there is a definite result. There could be different results, but you simply don’t know the result until you look at it.

Contrarily, in a quantum system, the answer to a question is not determined up until the time you ask it. And since it is not determined yet, you still can change the probabilities of measuring distinct states. (How cool would that be?)

Let’s say we have a qubit. Unless we observe its value, it is in a superposition state of 0 and 1. Once we observe its value, we will get 0 or 1. The chances of a qubit resulting in either one value don’t need to be 50–50. It can be 25–75, 67–33, or even 100–0. It can be any weighted probability distribution. This distribution depends on the qubit state: the quantum state.

In quantum mechanics, we use vectors to describe the quantum state. A popular way of representing quantum state vectors is the Dirac notation:
|ψ⟩.

In Python, we don’t have vectors. But we have arrays. Luckily, their structures are similar to that notation.

Let’s start with the simplest case. Let’s say we have a qubit that, when observed, always has the value 0. If you said this qubit must have the value 0 even before it is observed, you would be correct, yet imprecise. Before it is observed, this qubit has the probability of 1 (100%) to have the value 0 when observed.

These are the corresponding representations of two qubits that always result in 0 and 1 respectively when observed:

2- Coding the quantum states

Now, the fun part.

In order to put our experiment into code, we need to pick a programming language. My personal favorite is by no comparison Python.

Make sure to configure your environment by installing python itself, then you are ready to install the packages we need.

First, we install Qiskit:

pip install qiskit

Then, we install further packages and dependencies:

pip install numpy scipy matplotlib ipython pandas sympy nose seaborn

Finally, we install Scikit-Learn:

pip install scikit-learn

Let’s create our first qubit:

from qiskit import QuantumCircuit# Create a quantum circuit with one qubit
qc = QuantumCircuit(1)
# Define initial_state as |1>
initial_state = [0,1]
# Apply initialization operation to the qubit at position 0
qc.initialize(initial_state, 0)

QuantumCircuit is the fundamental unit of Qiskit. In this case, our circuit a single qubit.

We define [0,1] as the initial_state of our qubit and initialize the first and only qubit (at position 0 of the array) of our quantum circuit with it

Remember [0,1]? This is the equivalent to |1⟩ and in plain English, it is a qubit resulting in the value 1 when observed. Technically speaking, it implies that the qubit has a probability of 1 being of value 1, and a probability of 0 being of value 0.

Now, let’s simulate the circuit:

from qiskit import execute, Aer# Tell Qiskit how to simulate our circuit
backend = Aer.get_backend('statevector_simulator')
# Do the simulation, returning the result
result = execute(qc,backend).result()

statevector_simulator is one of Qiskit’s Aer most common backends.

The execute function runs our quantum circuit (qc) at the specified backend. It returns a job object that has a useful method job.result(). This outputs the result object once our program executes it.

Let’s have a look at our qubit in action.

Qiskit uses Matplotlib to provide useful visualizations. A simple histogram will do the job. The result object provides the get_counts method to obtain the histogram data of an executed circuit.

The method plot_histogram returns a Matplotlib figure showing that we have a 100% chance of observing the value 1.

from qiskit.visualization import plot_histogram
import matplotlib.pyplot as plt
# get the probability distribution
counts = result.get_counts()
# Show the histogram
plot_histogram(counts)
The qubit state

Now, let’s move on to a more advanced case. Say, we want our qubit to result in either 0 or 1 with the same probability (50-50).

In quantum mechanics, there is the fundamental principle superposition. It says any two (or more) quantum states can be added together (“superposed”), and the result will be another valid quantum state.

Wait! We already know two quantum states, |0⟩ and |1⟩. Why don’t we add them? |0⟩ and |1⟩ are vectors. Adding two vectors is straightforward.

Qubit superposition

Let’s experiment on our quantum system:

# Define state |psi>
initial_state = [1, 1]
# Redefine the quantum circuit
qc = QuantumCircuit(1)
# Initialise the 0th qubit in the state `initial_state`
qc.initialize(initial_state, 0)
# execute the qc
results = execute(qc,backend).result().get_counts()
# plot the results
plot_histogram(results)

Woah, what’s wrong here? The output shows:

{
"name": "QiskitError",
"message": "'Sum of amplitudes-squared does not equal one.'"}

The amplitudes are the values in our array. They are proportional to probabilities. And all the probabilities should add up to exactly 1 (100%). In the previous case, we had the vector [1, 1], and 1+1 = 2. We need to add weights to the quantum states |0⟩ and |1⟩. Let’s call them α and β.

These amplitudes are proportional to probabilities. We need to normalize them so that α² + β² = 1(why not α + β = 1? We will discuss that in a later article) . If both states |0⟩ and |1⟩ should have the same weight, then α = β. And therefore, we can solve our equation to α:

And we insert the value for both α and β (both are equal). Let’s try this quantum state:

from math import sqrt# Define state |psi>
initial_state = [1/sqrt(2), 1/sqrt(2)]
# Redefine the quantum circuit
qc = QuantumCircuit(1)
# Initialise the 0th qubit in the state `initial_state`
qc.initialize(initial_state, 0)
# execute the qc
results = execute(qc,backend).result().get_counts()
# plot the results
plot_histogram(results)
The output

3- Finally, we can talk pythonic quantum!

Phew. In this chapter, we introduced quite a few terms and equations just to scratch on the surface of quantum mechanics. But the actual source code is pretty neat, isn’t it?

We introduced the notion of the quantum state. In particular, the state of a binary quantum system. The quantum bit or qubit.

Until we observe a qubit, it is in superposition. Contrary to a classical bit that can be either 0 or 1, a qubit is in a superposition of both states. But once you observe it, there are distinct probabilities of measuring 0 or 1.

I hope you could make the algorithmic approach easy and fun to understand. There is of course a long way to go, but it always starts with baby steps!

--

--

Eya Abid

A CS Engineering Student, Aspiring To Enjoy The Algorithm Of Life.