# 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 import QuantumCircuit
from qiskit.circuit.library import HGate, CXGate
import numpy as np
Getting started with Dirac notation and qiskit circuits.
Qiskit Global Summer School 2023 - Lab 1
This lab shows you how to use Qiskit to implement some of the key concepts you learned in the first 3 lectures of the Qiskit Global Summer School 2023.
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:
= [[1],[0]] ket0
And we can use one of Qiskit’s visualisation tools to make our vectors nicer to look at:
array_to_latex(ket0)
$$
\[\begin{bmatrix} 1 \\ 0 \\ \end{bmatrix}\]$$
We can do the same with \(\langle0|\):
= [1,0]
bra0 array_to_latex(bra0)
$$
\[\begin{bmatrix} 1 & 0 \\ \end{bmatrix}\]$$
= [[0], [1]]
ket1 = [0 , 1] bra1
from qc_grader.challenges.qgss_2023 import grade_lab1_ex1
grade_lab1_ex1([ket1, bra1])
Submitting your answer. Please wait...
Congratulations 🎉! Your answer is correct and has been submitted.
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:
= Statevector(bra0)
sv_bra0
sv_bra0
Statevector([1.+0.j, 0.+0.j],
dims=(2,))
The Statevector
class has its own draw()
method:
'latex') sv_bra0.draw(
\[ |0\rangle\]
We can create more complex statevectors with multiple qubits like this:
= Statevector([1/2, 3/4, 4/5, 6/8])
sv_eq
'latex') sv_eq.draw(
\[\frac{1}{2} |00\rangle+\frac{3}{4} |01\rangle+\frac{4}{5} |10\rangle+\frac{3}{4} |11\rangle\]
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
Statevector
class
= Statevector([1/2, 1/2, 1/2, 1/2]) sv_valid
from qc_grader.challenges.qgss_2023 import grade_lab1_ex2
grade_lab1_ex2(sv_valid)
Submitting your answer. Please wait...
Congratulations 🎉! Your answer is correct and has been submitted.
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:
= Operator(bra0)
op_bra0
op_bra0
Operator([1.+0.j, 0.+0.j],
input_dims=(), output_dims=(2,))
The Operator class comes with some handy methods for working with operators, for example we can find the tensor product of 2 operators by using the tensor()
method:
= Operator(ket0)
op_ket0 op_bra0.tensor(op_ket0)
Operator([[1.+0.j, 0.+0.j],
[0.+0.j, 0.+0.j]],
input_dims=(), output_dims=(2, 2))
op_ket0
Operator([[1.+0.j],
[0.+0.j]],
input_dims=(), output_dims=(2,))
op_ket0.tensor(op_bra0)
Operator([[1.+0.j, 0.+0.j],
[0.+0.j, 0.+0.j]],
input_dims=(), output_dims=(2, 2))
We’ll use the Operator
and Statevector
classes more in the following exercises.
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:
= np.dot(op_bra0,op_ket0)
braket array_to_latex(braket)
$$
\[\begin{bmatrix} 1 \\ \end{bmatrix}\]$$
and the outer product \(|0\rangle\langle0|\) like this:
= np.outer(ket0,bra0)
ketbra array_to_latex(ketbra)
$$
\[\begin{bmatrix} 1 & 0 \\ 0 & 0 \\ \end{bmatrix}\]$$
= np.dot(op_bra0,op_ket0)
braket array_to_latex(braket)
$$
\[\begin{bmatrix} 1 \\ \end{bmatrix}\]$$
Note: the numpy methods we used above work with Qiskit Operators as well as regular python lists.
= np.dot(bra1,ket0)# put your answer for ⟨1|0⟩ here
bra1ket0
= np.dot(bra0,ket1)# put your answer for ⟨0|1⟩ here
bra0ket1
= np.dot(bra1,ket1)# put your answer for ⟨1|1⟩ here
bra1ket1
= np.outer(ket1,bra0)# put your answer for |1⟩⟨0| here
ket1bra0
= np.outer(ket0,bra1) # put your answer for |0⟩⟨1| here
ket0bra1
= np.outer(ket1,bra1)# put your answer for |1⟩⟨1| here ket1bra1
from qc_grader.challenges.qgss_2023 import grade_lab1_ex3
grade_lab1_ex3([bra1ket0, bra0ket1, bra1ket1, ket1bra0, ket0bra1, ket1bra1])
Submitting your answer. Please wait...
Congratulations 🎉! Your answer is correct and has been submitted.
np.dot(ket1, ket1)
Traceback (most recent call last):
Cell In[30], line 1
np.dot(ket1, ket1)
File <__array_function__ internals>:180 in dot
ValueError: shapes (2,1) and (2,1) not aligned: 1 (dim 1) != 2 (dim 0)
Use %tb to get the full traceback.
<p> 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? </p>
<p>a) $\vert 0\rangle$ and $\vert 1\rangle$ </p>
<p>b) $\vert 0\rangle$ and $\vert 0\rangle$ </p>
<p>c) $\vert 1\rangle$ and $\vert 1\rangle$ </p>
# add or remove your answer from this list
= ['a'] answer
from qc_grader.challenges.qgss_2023 import grade_lab1_ex4
grade_lab1_ex4(answer)
Submitting your answer. Please wait...
Congratulations 🎉! Your answer is correct and has been submitted.
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:
= Operator([[1,1],[0,0]])
m1 array_to_latex(m1)
$$
\[\begin{bmatrix} 1 & 1 \\ 0 & 0 \\ \end{bmatrix}\]$$
and similarly for m3:
= Operator([[0,1],[1,0]])
m3 array_to_latex(m3)
$$
\[\begin{bmatrix} 0 & 1 \\ 1 & 0 \\ \end{bmatrix}\]$$
We can also use builtin python mutliplication operations (e.g. @
, .dot
, or .matmul
) to check the following equation: $ M|a= f|a$
e.g. $ M1|0= f1|0$ = 0
@ket0) array_to_latex(m1
$$
\[\begin{bmatrix} 1 \\ 0 \\ \end{bmatrix}\]$$
= Operator([[1,0],[0,1]]) # create an operator for m2 here
m2 = Operator([[0,0],[1,1]])# create and operator for m4 here m4
from qc_grader.challenges.qgss_2023 import grade_lab1_ex5
grade_lab1_ex5([m2, m4])
Submitting your answer. Please wait...
Congratulations 🎉! Your answer is correct and has been submitted.
Probabilistic operations
A Controlled-NOT (or CNOT) operation is a probabilistic operation you can apply on 2 qubits.
Applying a CNOT on a state (X,Y) involves performing a NOT operation on Y when X is 1, otherwise do nothing. X is the control bit, Y is the target bit.
We can implement a CNOT gate (and many other quantum gates) using a class from Qiskit’s circuit library:
= CXGate()
cnot
array_to_latex(cnot)
$$
\[\begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & 0 & 1 \\ 0 & 0 & 1 & 0 \\ 0 & 1 & 0 & 0 \\ \end{bmatrix}\]$$
Note: this matrix is different from the one that appeared in the lesson because CXGate()
takes the right qubit to be the control rather than the left qubit.
Unitary Operations
An operator is unitary if: $ UU^{} = = U^{} 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:
= Operator(np.array([[ 0.50778085-0.44607116j, -0.1523741 +0.14128434j, 0.44607116+0.50778085j,
random -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
Operator
class that is not unitary
= Operator(np.array([[ 0.52778085-0.49607116j, -0.1523741 +0.14128434j, 0.44607116+0.50778085j,
non_unitary_op -0.14128434-0.1523741j ],
0.16855994+0.17151822j, 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]]))# create your operator here
from qc_grader.challenges.qgss_2023 import grade_lab1_ex6
grade_lab1_ex6(non_unitary_op)
Submitting your answer. Please wait...
Congratulations 🎉! Your answer is correct and has been submitted.
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)
$$
\[\begin{bmatrix} 0 & 1 \\ 1 & 0 \\ \end{bmatrix}\]$$
Pauli Y (\(\sigma_y\)):
= Pauli('Y')
pauli_y
array_to_latex(pauli_y)
$$
\[\begin{bmatrix} 0 & - i \\ i & 0 \\ \end{bmatrix}\]$$
Pauli Z (\(\sigma_z\)), the phase flip:
= Pauli('Z')
pauli_z
array_to_latex(pauli_z)
$$
\[\begin{bmatrix} 1 & 0 \\ 0 & -1 \\ \end{bmatrix}\]$$
We can use the Operator
class with the Pauli
class:
= Operator(pauli_x)
op_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\)
= np.dot(op_x,ket0)
op_new
array_to_latex(op_new)
$$
\[\begin{bmatrix} 0 \\ 1 \\ \end{bmatrix}\]$$
= Operator(pauli_z)
op_z
op_z
Operator([[ 1.+0.j, 0.+0.j],
[ 0.+0.j, -1.+0.j]],
input_dims=(2,), output_dims=(2,))
= np.dot(op_z, ket1) # do your operations here result
from qc_grader.challenges.qgss_2023 import grade_lab1_ex7
grade_lab1_ex7(result)
Submitting your answer. Please wait...
Congratulations 🎉! Your answer is correct and has been submitted.
Qubit Unitary Operations - Hadamard
The Hadamard gate is one of the most important unitary operations in quantum computing. We can implement a Hadamard gate (and many other quantum gates) using a class from Qiskit’s circuit library:
= HGate()
hadamard
array_to_latex(hadamard)
$$
\[\begin{bmatrix} \frac{\sqrt{2}}{2} & \frac{\sqrt{2}}{2} \\ \frac{\sqrt{2}}{2} & - \frac{\sqrt{2}}{2} \\ \end{bmatrix}\]$$
You can convert many Qiskit classes to operators to make use of functions specific to the Operator
class, such as is_unitary
= Operator(hadamard)
hop hop.is_unitary()
True
Quantum Circuits
In the lectures you learned how to create a Quantum Circuit using a CNOT and a Hadamard gate. This circuit creates the Bell State \(|\phi^+\rangle\). We can implement this using Qiskit’s QuantumCircuit
class:
= QuantumCircuit(2)
bell
0) # apply an H gate to the circuit
bell.h(0,1) # apply a CNOT gate to the circuit
bell.cx(
="mpl") bell.draw(output
Matplotlib is building the font cache; this may take a moment.
If we want to check what the matrix representation is of this quantum state we can convert the circuit directly to an operator:
= Operator(bell)
bell_op
array_to_latex(bell_op)
$$
\[\begin{bmatrix} \frac{\sqrt{2}}{2} & \frac{\sqrt{2}}{2} & 0 & 0 \\ 0 & 0 & \frac{\sqrt{2}}{2} & - \frac{\sqrt{2}}{2} \\ 0 & 0 & \frac{\sqrt{2}}{2} & \frac{\sqrt{2}}{2} \\ \frac{\sqrt{2}}{2} & - \frac{\sqrt{2}}{2} & 0 & 0 \\ \end{bmatrix}\]$$
= QuantumCircuit(3)
ghz
##############################
# add gates to your circuit here
0)
ghz.h(0, 1)
ghz.cx(0, 2)
ghz.cx(
##############################
='mpl') ghz.draw(output
from qc_grader.challenges.qgss_2023 import grade_lab1_ex8
grade_lab1_ex8(ghz)
Submitting your answer. Please wait...
Congratulations 🎉! Your answer is correct and has been submitted.
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:
$ |+= |0+ |1$
The probability of measuring 0 or 1 is given by the following:
$ Pr(0) = ||^2 = $
$ Pr(1) = ||^2 = $
Let’s create a \(|+\rangle\) using the Statevector
class:
= Statevector.from_label("+")
plus_state
'latex') plus_state.draw(
\[\frac{\sqrt{2}}{2} |0\rangle+\frac{\sqrt{2}}{2} |1\rangle\]
plus_state
Statevector([0.70710678+0.j, 0.70710678+0.j],
dims=(2,))
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
= plus_state.measure()
res
res
('0',
Statevector([1.+0.j, 0.+0.j],
dims=(2,)))
We can implement the same \(|+\rangle\) state with measurement using a quantum circuit:
= QuantumCircuit(1,1)
qc 0)
qc.h(0, 0)
qc.measure(
="mpl") qc.draw(output
If we ran this circuit using a simulator we would get the same results as we did with the statevector class.
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 2-qubit Bell State \(|\phi^+\rangle\) :
= Statevector([np.sqrt(1/2), 0, 0, np.sqrt(1/2)])
sv_bell
'latex') sv_bell.draw(
\[\frac{\sqrt{2}}{2} |00\rangle+\frac{\sqrt{2}}{2} |11\rangle\]
sv_bell.probabilities_dict()
{'00': 0.5000000000000001, '11': 0.5000000000000001}
= Statevector([0,np.sqrt(1/2), np.sqrt(1/2), 0])# create a statevector for |𝜓+⟩ here
sv_psi_plus =sv_psi_plus.probabilities_dict() # find the measurement probabilities for |𝜓+⟩ here
prob_psi_plus
= Statevector([0,np.sqrt(1/2), -np.sqrt(1/2), 0])# create a statevector for |𝜓−⟩ here
sv_psi_minus = sv_psi_minus.probabilities_dict()# find the measurement probabilities for |𝜓−⟩ here
prob_psi_minus
= Statevector([np.sqrt(1/2), 0, 0, -np.sqrt(1/2)])# create a statevector for |𝜙−⟩ here
sv_phi_minus = sv_phi_minus.probabilities_dict()# find the measurement probabilities for |𝜙−⟩ here prob_phi_minus
from qc_grader.challenges.qgss_2023 import grade_lab1_ex9
grade_lab1_ex9([prob_psi_plus, prob_psi_minus, prob_phi_minus])
Submitting your answer. Please wait...
Congratulations 🎉! Your answer is correct and has been submitted.
Final Challenge - generate a QFT circuit
The Fourier transform occurs in many different formats throughout classical computing, in areas ranging from signal processing to data compression to complexity theory. The quantum Fourier transform (QFT) is the quantum implementation of the discrete Fourier transform over the amplitudes of a wavefunction. It is part of many quantum algorithms, most notably Shor’s factoring algorithm and quantum phase estimation. You’ll learn more about this important implementation later on during the Summer School, but for this final challenge of Lab 1 we would like you to use Qiskit to create the following QFT circuit on 2 qubits:
= QuantumCircuit(2)
qft
##############################
# add gates to your circuit here
0)
qft.h(
/2, 0, 1)
qft.cp(np.pi
1)
qft.h(
##############################
='mpl') qft.draw(output
from qc_grader.challenges.qgss_2023 import grade_lab1_ex10
grade_lab1_ex10(qft)
Submitting your answer. Please wait...
Congratulations 🎉! Your answer is correct and has been submitted.
To see the matrix that describes the action of this circuit, we can plug the circuit into the Operator
function like this:
= Operator(qft)
U
array_to_latex(U)
$$
\[\begin{bmatrix} \frac{1}{2} & \frac{1}{2} & \frac{1}{2} & \frac{1}{2} \\ \frac{1}{2} & - \frac{1}{2} & \frac{i}{2} & - \frac{i}{2} \\ \frac{1}{2} & \frac{1}{2} & - \frac{1}{2} & - \frac{1}{2} \\ \frac{1}{2} & - \frac{1}{2} & - \frac{i}{2} & \frac{i}{2} \\ \end{bmatrix}\]$$
Congratulations! You finished Lab 1 of the Qiskit Global Summer School 2023! 🎉 🎉 🎉