Part I: Introduction to Qiskit¶
Welcome to Qiskit! Before starting with the exercises, please run the cell below by pressing 'shift' + 'return'. You can run the other following cells in the same way.
import numpy as np
from numpy import pi
# Importing standard Qiskit libraries
from qiskit import QuantumCircuit, transpile, assemble, Aer, IBMQ, execute
from qiskit.quantum_info import Statevector, random_statevector
from qiskit.visualization import plot_bloch_multivector, plot_histogram
#from qiskit_textbook.problems import dj_problem_oracle
# Additional required imports:
from qiskit.visualization import array_to_latex
from qiskit.quantum_info import Statevector, random_statevector
from qiskit.quantum_info.operators import Operator, Pauli
from qiskit.circuit.library import HGate, CXGate
A. Basics: Vectors and Dirac Notation¶
In the lectures you learned different ways of representing quantum states, including how to use bra-ket (Dirac) notation.
Although bra-ket notation cannot be represented exactly in code, we can represent their vector and matrix equivalent with python.
E.g. we can represent $|0\rangle$ using a python list:
ket0 = [[1],[0]]
And we can use one of Qiskit's visualisation tools to make our vectors nicer to look at:
array_to_latex(ket0)
We can do the same with $\langle0|$:
bra0 = [1,0]
array_to_latex(bra0)
Ex 1 - create $|1\rangle$ and $\langle1|$ with python lists
ket1 = # put your answer answer here for |1⟩
bra1 = # put answer here for ⟨1|
# Validate your answer
array_to_latex(ket1)
array_to_latex(bra1)
A.1 Qiskit Statevector
Class¶
In the lectures you learned about using state vectors to represent quantum states. You can represent quantum state vectors in code using Qiskit's Statevector
class.
Qiskit's Statevector
class can take different forms of input (e.g. python list, numpy array, another state vector) to construct a state vector.
Let's take the bra0
object we created earlier and convert it to a Statevector
object:
sv_bra0 = Statevector(bra0)
sv_bra0
Statevector([1.+0.j, 0.+0.j], dims=(2,))
The Statevector
class has its own draw()
method:
sv_bra0.draw('latex')
# Validate your answers for Ex1 i.e., ket1
sv_ket1 = Statevector(ket1)
sv_ket1.draw('latex')
We can create more complex statevectors with multiple qubits like this:
sv_eq = Statevector([1/2, 3/4, 4/5, 6/8])
sv_eq.draw('latex')
Note that the vector above is not a valid state vector as it is not normalised.
We can check this with the is_valid()
method:
sv_eq.is_valid()
False
Ex 2 - create your own valid statevector object using the Statevector
class
sv_valid = # create your statevector here
sv_valid.draw('latex')
# Validate your answer
True
A.2 Qiskit Operator
Class¶
The Operator
class is used in Qiskit to represent matrix operators acting on a quantum system. It has several methods to build composite operators using tensor products of smaller operators, and to compose operators.
One way we can initialise a Qiskit Operator
is by using a python list, like the one we created earlier:
op_ket0 = Operator(ket0)
op_bra0 = Operator(bra0)
We'll use the Operator
and Statevector
classes more in the following exercises.
A.3 Inner & Outer Product¶
In the lectures you covered the concepts of the inner and outer product. We can explore these concepts in code using numpy methods .dot()
(the inner product is a generalised form of the dot product) and .outer()
.
For example, we can find the inner product $\langle0|0\rangle$ like this:
#braket = np.dot(op_bra0,op_ket0)
braket = np.dot(bra0,ket0)
array_to_latex(braket)
and the outer product $|0\rangle\langle0|$ like this:
ketbra = np.outer(ket0,bra0)
array_to_latex(ketbra)
# Check your answer for two orthogonal states
op_ket1 = Operator(ket1)
op_bra1 = Operator(bra1)
braket = np.dot(op_bra1,op_ket0)
array_to_latex(braket)
Ex 3 - use numpy to find the result of the following inner and outer products: $\langle1|0\rangle, \langle0|1\rangle, \langle1|1\rangle, |1\rangle\langle0|, |0\rangle\langle1|$ and $|1\rangle\langle1| $
#bra1ket0 = # put your answer for ⟨1|0⟩ here
#bra0ket1 = # put your answer for ⟨0|1⟩ here
#bra1ket1 = # put your answer for ⟨1|1⟩ here
#ket1bra0 = # put your answer for |1⟩⟨0| here
#ket0bra1 = # put your answer for |0⟩⟨1| here
#ket1bra1 = # put your answer for |1⟩⟨1| here
#Validate your answer
Validation: From the Lecture notes we know that "The matrix multiplication $∣b⟩⟨a∣,\vert b\rangle \langle a\vert,∣b⟩⟨a∣$, one obtains a square matrix having a 1 in the entry corresponding to the pair (b,a),(b,a),(b,a), respectively"; meaning that the row of the entry corresponds to b and the column corresponds to a, and 0 for all other entries."
Ex 4 - when the inner product of 2 quantum states is equal to 0, those states are orthogonal. Which of the following states are orthogonal?
a) $\vert 0\rangle$ and $\vert 1\rangle$ b) $\vert 0\rangle$ and $\vert 0\rangle$ c) $\vert 1\rangle$ and $\vert 1\rangle$# add or remove your answer from this list
answer = ['a', 'b', 'c']
A.4 Deterministic operations¶
As mentioned in the lectures, there are 4 single bit deterministic operations:
f1 = constant-0
f2 = identity
f3 = bit flip / not
f4 = constant-1
$$ \begin{array}{c|c} a & f_1(a)\\ \hline 0 & 0\\ 1 & 0 \end{array} \qquad \begin{array}{c|c} a & f_2(a)\\ \hline 0 & 0\\ 1 & 1 \end{array} \qquad \begin{array}{c|c} a & f_3(a)\\ \hline 0 & 1\\ 1 & 0 \end{array} \qquad \begin{array}{c|c} a & f_4(a)\\ \hline 0 & 1\\ 1 & 1 \end{array} $$
We can create Qiskit Operators for these 4 operations, by passing their matrix representations as arguments to the Operator
class.
E.g. for constant-0 we can create the corresponding matrix m1 like so:
m1 = Operator([[1,1],[0,0]])
array_to_latex(m1)
--------------------------------------------------------------------------- NameError Traceback (most recent call last) /home/andrew/cs495_595/docs/Assignments/Lab_1/Lab1_Problems.ipynb Cell 43 line 1 ----> <a href='vscode-notebook-cell://wsl%2Bubuntu/home/andrew/cs495_595/docs/Assignments/Lab_1/Lab1_Problems.ipynb#X60sdnNjb2RlLXJlbW90ZQ%3D%3D?line=0'>1</a> m1 = Operator([[1,1],[0,0]]) <a href='vscode-notebook-cell://wsl%2Bubuntu/home/andrew/cs495_595/docs/Assignments/Lab_1/Lab1_Problems.ipynb#X60sdnNjb2RlLXJlbW90ZQ%3D%3D?line=1'>2</a> array_to_latex(m1) NameError: name 'Operator' is not defined
and similarly for m3:
m3 = Operator([[0,1],[1,0]])
array_to_latex(m3)
We can also use builtin python mutliplication operations (e.g. @
, .dot
, or .matmul
) to check the following equation: $ M|a\rangle = f|a\rangle $
e.g. $ M1|0\rangle = f1|0\rangle $ = 0
array_to_latex(m1@ket0)
--------------------------------------------------------------------------- NameError Traceback (most recent call last) /home/andrew/cs495_595/docs/Assignments/Lab_1/Lab1_Problems.ipynb Cell 47 line 1 ----> <a href='vscode-notebook-cell://wsl%2Bubuntu/home/andrew/cs495_595/docs/Assignments/Lab_1/Lab1_Problems.ipynb#X64sdnNjb2RlLXJlbW90ZQ%3D%3D?line=0'>1</a> array_to_latex(m1@ket0) NameError: name 'array_to_latex' is not defined
# Validate your Answer
array_to_latex(m1.dot(ket1))
--------------------------------------------------------------------------- NameError Traceback (most recent call last) /home/andrew/cs495_595/docs/Assignments/Lab_1/Lab1_Problems.ipynb Cell 48 line 2 <a href='vscode-notebook-cell://wsl%2Bubuntu/home/andrew/cs495_595/docs/Assignments/Lab_1/Lab1_Problems.ipynb#X65sdnNjb2RlLXJlbW90ZQ%3D%3D?line=0'>1</a> # Validate your Answer ----> <a href='vscode-notebook-cell://wsl%2Bubuntu/home/andrew/cs495_595/docs/Assignments/Lab_1/Lab1_Problems.ipynb#X65sdnNjb2RlLXJlbW90ZQ%3D%3D?line=1'>2</a> array_to_latex(m1.dot(ket1)) NameError: name 'array_to_latex' is not defined
from numpy import array
M1 = array([[1, 1],
[0, 0]])
M1 # way to print the operator, too.
array([[1, 1], [0, 0]])
from numpy import matmul
display(matmul(M1, ket1))
--------------------------------------------------------------------------- NameError Traceback (most recent call last) /home/andrew/cs495_595/docs/Assignments/Lab_1/Lab1_Problems.ipynb Cell 50 line 3 <a href='vscode-notebook-cell://wsl%2Bubuntu/home/andrew/cs495_595/docs/Assignments/Lab_1/Lab1_Problems.ipynb#Y100sdnNjb2RlLXJlbW90ZQ%3D%3D?line=0'>1</a> from numpy import matmul ----> <a href='vscode-notebook-cell://wsl%2Bubuntu/home/andrew/cs495_595/docs/Assignments/Lab_1/Lab1_Problems.ipynb#Y100sdnNjb2RlLXJlbW90ZQ%3D%3D?line=2'>3</a> display(matmul(M1, ket1)) NameError: name 'ket1' is not defined
(M1 + M1 ) / 2
array([[1., 1.], [0., 0.]])
Ex 5 - create Qiskit Operators for m2 and m4 (hint: check out the lectures to find the appropriate matrices)
m2 = # create an operator for m2 here
m4 = # create and operator for m4 here
# Validate your answers
Remark: Always make sure you check the conventions (assumtions) of the SDK used!¶
B: Basic Rotations (Operators) on One Qubit and the Bloch Sphere¶
Before getting into complicated circuits on many qubits, let us start by looking at a single qubit. Read this chapter: https://qiskit.org/textbook/ch-states/introduction.html
It will help you to learn the basics about the Bloch sphere, Pauli operators, as well as the Hadamard gate and the $S$ and $S^\dagger$ gates.
By default, states in Qiskit start in $|0\rangle$, which corresponds to "arrow up" on the Bloch sphere. Play around with the gates $X$, $Y$, $Z$, $H$, $S$ and $S^\dagger$ to get a feeling for the different rotations. To do so, insert combinations of the following code lines in the lines indicated in the program:
qc.x(0) # rotation by Pi around the x-axis
qc.y(0) # rotation by Pi around the y-axis
qc.z(0) # rotation by Pi around the z-axis
qc.s(0) # rotation by Pi/2 around the z-axis
qc.sdg(0) # rotation by -Pi/2 around the z-axis
qc.h(0) # rotation by Pi around an axis located halfway between x and z
Try to reach the given state in the Bloch sphere in each of the following exercises:

Ex 6. Let us start easy by performing a bit flip : $|0 \rangle$
def lab1_ex6():
qc = QuantumCircuit(1)
#
#
# Flipt the state to |1> :
#
#
return qc
state = Statevector.from_instruction(lab1_ex6())
plot_bloch_multivector(state)
# Validate your answer
Ex 7. Next, we would like to create superposition. The goal is to reach the state $|+\rangle = \frac{1}{\sqrt{2}}\left(|0\rangle + |1\rangle\right)$.

def lab1_ex7():
qc = QuantumCircuit(1)
#
#
# FILL YOUR CODE IN HERE
#
#
return qc
state = Statevector.from_instruction(lab1_ex7())
plot_bloch_multivector(state)
# Validate your answer
Ex 8. Let's combine the two operations seen before. The goal is to reach the state $|-\rangle = \frac{1}{\sqrt{2}}\left(|0\rangle - |1\rangle\right)$.

Can you even come up with different ways?
def lab1_ex8():
qc = QuantumCircuit(1)
#
#
# FILL YOUR CODE IN HERE
#
#
return qc
state = Statevector.from_instruction(lab1_ex8())
plot_bloch_multivector(state)
# Validate your answer
Ex 9. Finally, we move on to the complex numbers. The goal is to reach the state $|- i\rangle = \frac{1}{\sqrt{2}}\left(|0\rangle - i|1\rangle\right)$.

def lab1_ex9():
qc = QuantumCircuit(1)
#
# FILL YOUR CODE IN HERE
#
#
return qc
state = Statevector.from_instruction(lab1_ex9())
plot_bloch_multivector(state)
# Validate your answer
Ex 10. One more problem with complex numbers. The goal is to reach the state $| i\rangle = \frac{1}{\sqrt{2}}\left(|0\rangle + i|1\rangle\right)$.

def lab1_ex10():
qc = QuantumCircuit(1)
#
# FILL YOUR CODE IN HERE
#
#
return qc
state = Statevector.from_instruction(lab1_ex10())
plot_bloch_multivector(state)
# Validate your answer
C. Unitary Operations¶
An operator is unitary if: $ UU^{\dagger} = \mathbb{1} = U^{\dagger} U$
We can check if an operator is Unitary using Qiskit with the is_unitary()
method:
m3.is_unitary()
True
With small operators like m3 we could probably figure this out easily by ourselves, but with more complex operators it becomes more convenient to use the Qiskit function:
Motivation for the introcution to Phase (pp 93., Nielsen and Chuang, Quantum Information and Quantum Computation.)¶
random = Operator(np.array([[ 0.50778085-0.44607116j, -0.1523741 +0.14128434j, 0.44607116+0.50778085j,
-0.14128434-0.1523741j ],
[ 0.16855994+0.12151822j, 0.55868196+0.38038841j, -0.12151822+0.16855994j,
-0.38038841+0.55868196j],
[ 0.50778085-0.44607116j, -0.1523741 +0.14128434j, -0.44607116-0.50778085j,
0.14128434+0.1523741j ],
[ 0.16855994+0.12151822j, 0.55868196+0.38038841j, 0.12151822-0.16855994j,
0.38038841-0.55868196j]]))
random.is_unitary()
True
Ex 11 - create an operator using the Operator
class that is not unitary
non_unitary_op = # create your operator here
# Validate your answer
C.1 Qubit Unitary Operations - Pauli Operations¶
Some of the most common unitary operations in quantum computing are the Pauli operations. Qiskit's Pauli
classes make it easy to interact with Pauli operators in code:
E.g. Pauli X ($\sigma_x$), the bit flip:
pauli_x = Pauli('X')
array_to_latex(pauli_x)
Pauli Y ($\sigma_y$):
pauli_y = Pauli('Y')
array_to_latex(pauli_y)
Pauli Z ($\sigma_z$), the phase flip:
pauli_z = Pauli('Z')
array_to_latex(pauli_z)
We can use the Operator
class with the Pauli
class:
op_x = Operator(pauli_x)
op_x
Operator([[0.+0.j, 1.+0.j], [1.+0.j, 0.+0.j]], input_dims=(2,), output_dims=(2,))
Let's use the Operator
class and numpy to find the outcome of $\sigma_x|0\rangle$
op_new = np.dot(op_x,ket0)
array_to_latex(op_new)
Ex 12 - Apply the Pauli-Z operator on $|1\rangle$
result = np.dot(Operator(pauli_z),ket1) # do your operations here
# Validate your answer: a two step process
# Step 1
#Step 2:
#qc.draw()
┌───┐┌───┐ q: ┤ X ├┤ Z ├ └───┘└───┘
Remark: You can convert many Qiskit classes to operators to make use of functions specific to the Operator
class, such as is_unitary
¶
zop = Operator(z)
zop.is_unitary()
True
C.2 Measuring Quantum states¶
As explained in the lectures you can find the probability of measurement outcomes by taking the absolute value squared of the entries of a quantum state vector.
For example, when measuring the + state:
$ |+\rangle = \frac{1}{\sqrt2}|0\rangle + \frac{1}{\sqrt2}|1\rangle $
The probability of measuring 0 or 1 is given by the following:
$ Pr(0) = |\frac{1}{\sqrt2}|^2 = \frac{1}{2}$
$ Pr(1) = |\frac{1}{\sqrt2}|^2 = \frac{1}{2}$
Let's create a $|+\rangle$ using the Statevector
class:
plus_state = Statevector.from_label("+")
plus_state.draw('latex')
Now we can get the probability of measuring 0 or 1:
plus_state.probabilities_dict()
{'0': 0.4999999999999999, '1': 0.4999999999999999}
The dictionary object above shows you all the possible measurement outcomes and what the probability is of getting them. The actual act of measuring forces the state to collapse into either the 0 or 1 state:
# run this cell multiple times to show collapsing into one state or the other
res = plus_state.measure()
res
('0', Statevector([1.+0.j, 0.+0.j], dims=(2,)))
We can implement the same $|+\rangle$ state with measurement using a quantum circuit:
qc = QuantumCircuit(1,1)
qc.h(0)
qc.measure(0, 0)
qc.draw(output="mpl")
In the next example, let's use the Statevector
class to find the measurement outcomes for a dependent, probabilistic state. We'll find the measurement probilities for the application of the Pauli-Z operator on $|1\rangle$
from qiskit.circuit.library import HGate, CXGate, ZGate
#define a function
def lab1_example():
qc = QuantumCircuit(1)
#
# FILL YOUR CODE IN HERE
qc.x(0)
z = ZGate()
qc.z(0)
#
#
return qc
# Validate your answer: Stgep 1
qc_z1 = QuantumCircuit(1)
qc_z1 = lab1_example()
qc_z1.draw(output="mpl")
#sv_qc_z1 = Statevector([0, -1])
sv_qc_z1.draw('latex')
sv_qc_z1.probabilities_dict()
{'1': 1.0}
Ex 13 - Using the Statevector class find the probabilities for the following state :
Pauli-X operator on $|1\rangle$,
Ex 14 - Using the Statevector class find the probabilities for the following state :Pauli-Y operator on $|0\rangle$
Ex 15 - Using the Statevector class find the probabilities for the following state : Pauli-Y operator on $|1\rangle$
import qiskit.tools.jupyter
%qiskit_version_table
#import qiskit
#qiskit.__qiskit_version__
Version Information
Qiskit Software | Version |
---|---|
qiskit-terra | 0.24.2 |
qiskit-aer | 0.12.2 |
qiskit-ignis | 0.7.1 |
qiskit-ibmq-provider | 0.20.2 |
qiskit | 0.43.3 |
qiskit-nature | 0.4.2 |
qiskit-machine-learning | 0.4.0 |
System information | |
Python version | 3.9.12 |
Python compiler | Clang 12.0.0 |
Python build | main, Apr 5 2022 01:53:17 |
OS | Darwin |
CPUs | 4 |
Memory (Gb) | 32.0 |
Thu Aug 24 20:34:02 2023 EDT |