COMS 4281 Problem Set 5
COMS 4281 – Intro to Quantum Computing
Problem Set 5: Noise and Codes
Due: December 11, 11�59pm.
Collaboration is allowed and encouraged (teams of at most 3). Please read the syllabus carefully for the guidlines regarding collaboration. In particular, everyone must
write their own solutions in their own words.
Write your collaborators here:
Problem 1: Mixed states
Problem 1.1
Compute the single- and two-qubit reduced density matrices of the -qubit GHZ state
Problem 1.2
Let denote a probabilistic mixture of states — not necessarily orthogonal — where ‘s are probabilities that sum to . Consider the
density matrix on a system . Show that the following vector is a valid pure state (i.e. unit length vector), and it purifies :
where is a register of dimension at least and is an orthonormal basis for .
|000⟩ + |111⟩
{(p1, |ψ1⟩), … , (ψk, |ψk⟩)} pi 1
σ = p1 |ψ1⟩ ⟨ψ1| + ⋯ + pk |ψk⟩ ⟨ψk| A |θ⟩AB σ
√pi|ψi⟩A ⊗ |i⟩B
B k {|i⟩} B
Problem 1.3
Come up with a two-qubit pure state whose reduced density matrix on system is
where is some parameter.
Hint: Try to reduce to part (2) above.
Problem 1.4
Let denote an unknown qubit, and suppose it was just teleported from Alice to Bob. But imagine that, before Alice is able to tell Bob the outcomes
of her measurements, her internet cuts out. So Bob’s qubit is one of the states
with equal probability. What is the density matrix that describes the mixed state of Bob’s qubit?
Problem 2: Limitations of Shor’s code
Show that there are distinct two-qubit errors (unitaries acting on two qubits) such that
where represent the logical state using the -qubit Shor code. This shows that the Shor code is not capable of correcting more than error.
Problem 3: An error detection code
Shor’s -qubit code can correct any single-qubit error on one of its qubits. Below we give a -qubit code which can \emph{detect} any single-qubit error on its
qubits. By this we mean that there is a detection circuit that determines if a single-qubit error has occurred, but it can’t necessarily identify which one has occurred.
TrB(|ψ⟩ ⟨ψ|) = (
1 + p −√(1 − p)p
−√(1 − p)p 1 − p
|ψ⟩ = α |0⟩ + β |1⟩
|ψ⟩ , X |ψ⟩ , Z |ψ⟩ , XZ |ψ⟩
0⟩ = E2 ∣∣
1⟩ |0⟩ , |1⟩ 9 1
The encoding map is as follows:
Problem 3.1
Give an example of two distinct single-qubit unitaries (which may act on different qubits) such that
This shows that this code cannot uniquely identify single-qubit errors, because given , one can’t be sure whether the original state was or . However, in
the next few parts you will show that the code can still detect that some error has occurred.
Problem 3.2
Give a procedure (either as a quantum circuit or sufficiently detailed pseudocode) that detects a \emph{bitflip} error on a single qubit of an encoded state
. In other words the procedure, if it’s given a valid encoded state , returns and outputs “No error”, whereas if it’s given where is the
bitflip operation on some qubit , then the circuit outputs ” error somewhere”.
Problem 3.3
Give a procedure (either as a quantum circuit or sufficiently detailed pseudocode) that detects a phaseflip error on a single qubit of an encoded state .
Problem 3.4
Explain how to detect any unitary -qubit error on one of the qubits.
0⟩ = ( |00⟩ + |11⟩) ⊗ ( |00⟩ + |11⟩)
1⟩ = ( |00⟩ − |11⟩) ⊗ ( |00⟩ − |11⟩) .
0⟩ = E2 ∣∣
Problem 4: Circuits on noisy quantum computers
The IBM Quantum Lab gives public access to a number of their quantum computers. In this problem you’ll get to play with them — and see the effects of noise on your
quantum circuits.
Problem 4.1 – Benchmarking individual qubits
In this subproblem, we will benchmark individual qubits on (a simulation of) the 5-qubit ibmq_essex device. A typical way to benchmark a qubit is to run a sequence
of randomly chosen single-qubit gates , and then running the reverse sequence so that overall the effect should be the identity. Of
course, each gate will incur some noise, so the state of the qubit will drift over time. One can measure the noise by measuring the qubit at the end of the sequence to
see if it stayed in the state.
You will write code to perform the following: for , for each qubit , pick a sequence of gates where each is
chosen randomly from the gate set . Then, on qubit run the sequence forward and then in reverse, and measure the qubit. Do this 1000 times and
calculate the percentage of times that the qubit ends back in the state.
Below, we’ve provided two functions. The first function, benchmark_qubit , requires you to fill in some code, and it returns a IBMQJob object. The second function,
retrieve_job_results , takes an IBMQJob object and gets the measurement counts. (The reason we’re dividing this up into two steps is because later we’ll run
these circuits on a real device).
g1, g2, … , gk g
k−1, … , g
k = 10, 20, 30, … , 100 q = 0, 1, 2, … , 4 g1, … , gk gi
{X, Y , Z, H} q (gi)
pq,k q |0⟩
In [2]: from qiskit import IBMQ, transpile
from qiskit import QuantumCircuit
from qiskit.providers.aer import AerSimulator
from qiskit.providers.fake_provider import FakeEssex
from qiskit.providers.jobstatus import JobStatus
import numpy as np
from matplotlib import pyplot as plt
##########
# This function takes as input
# – q : qubit number 0 thru 4
# – k : length of random gate sequence
# – shots: number of times to run and measure the sequence
# – device: the device (either simulated or real) to run the circuit on
# Returns:
# – the IBMQJob object corresponding to the circuit (see https://qiskit.org/documentation/stubs/qiskit.providers.ibmq.job.IBMQJob.html#qis
##########
def benchmark_qubit(q, k, shots,device):
circ = QuantumCircuit(5, 1)
### WRITE CODE TO GENERATE THE RANDOM SEQUENCE OF LENGTH k, and ITS REVERSAL, ON QUBIT q ####################
### END CODE BLOCK ###################
Below, write code using benchmark_qubit , retrieve_job_results to get the measurement counts and calculate for and
. Then, plot against (you can consult https://www.geeksforgeeks.org/using-matplotlib-with-jupyter-notebook/ for an example on how to
plot graphs). You should have 5 plots in one graph.
# measure qubit q, and store it in classical register [0]
circ.measure([q], [0])
#this will break down the circuit into gates that can be implemented on the chip
compiled_circuit = transpile(circ,device,optimization_level=0)
job = device.run(compiled_circuit,shots=shots)
return job
##########
# This function takes as input
# – job: the IBMQJob object corresponding to a circuit being executed
# – blocking: if True, then this will wait until the circuit results are done executing on the device
# (either fake or real). If False, then this will first check the status of the job.
# Returns:
# – if the job is done, then it returns the counts as a dictionary (e.g., { ‘0000’: 356, ‘0001’: 288, …})
# otherwise if the job is still running or some other status, then it returns None
##########
def retrieve_job_results(job,blocking=True):
#if it’s blocking, then just go ahead and call result()
if blocking:
counts = job.result().get_counts(0)
return counts
#first, check the status
job_status = job.status()
if job_status is JobStatus.DONE:
counts = job.result().get_counts(0)
return counts
print(“The job “,job.job_id,” has status: “,job_status)
return None
pq,k q = 0, 1, 2, 3, 4
k = 10, 20, 30, … , 100 pq,k k
fake_device = AerSimulator.from_backend(FakeEssex())
#### WRITE YOUR CODE HERE #################
#example code,
job = benchmark_qubit(0,100,1000,fake_device)
counts = retrieve_job_results(job)
print(counts)
{‘1’: 20, ‘0’: 980}
Problem 4.2 – Estimating the single qubit gate noise
For each qubit , find a best function (linear, quadratic, exponential,…) that fits the plots. For example, if the plot of against looks linear, then you should
come up with parameters such that is close to . If it looks like exponential decay, then you should find an approximate for some
parameters .
This function can be used to give a simple model for how noise accumulates on each qubit from single-qubit gates.
#an example plot, change to display your data instead
for q in range(5):
ks = np.arange(100)
p_qks = [k*q + q*np.sin(k) for k in ks]
plt.plot(ks, p_qks, label=f’q={q}’)
plt.xlabel(‘$k$’)
plt.ylabel(‘$kq + q sin(k)$’)
plt.title(‘$kq + q sin(k)$ vs. k’)
plt.legend()
plt.show()
#### END CODE BLOCK ############################################################
q f(k) pqk k
a, b pqk ak + b f(k) = ae
Problem 4.3 – Benchmarking entanglement generation
Now we benchmark the quantum computer on more complex circuits that involve entangling gates, which will generally be more noisy than single-qubit gates.
For each , let denote the -qubit GHZ state
When , this is simply the EPR pair we know and love. Let denote a circuit that starts with zeroes and outputs .
Write code to do the same benchmarking as in Problem 4.1, except now we perform , then , then , and so on times. Ideally, all of these circuits would cancel
out so measuring all qubits will yield zero. However, the gates will be noisy so error will accumulate as grows larger.
Let denote the percentage of times (out of 1000 shots) that doing and for times yields all zeroes in the qubits. Plot versus for and
for . There should be 4 plots on one graph.
Important: the ibmq_essex device has a very particular connectivity of qubits:
You can only apply 2-qubit gates between the connected nodes. For exampe, you can apply a CNOT between qubits 3 and 1, but not 3 and 2. Thus, when coding your
circuit , you may want to judiciously choose which qubits you use to maximize the performance. The darker shading of a qubit means lower noise rate.
r = 2, 3, 4, 5 |ψr⟩ r
r = 2 Cr r |ψr⟩
r k r prk k r = 2, 3, 4, 5
k = 10, 20, 30, … , 100
In [ ]: ##########
# This function takes as input
# – r : size of GHZ state
# – k : length of random gate sequence
# – shots: number of times to run and measure the sequence
# – device: the device to run on, either simulated or real
Problem 4.4 – Estimating noise of the GHZ circuit
For each find a best function (linear, quadratic, exponential,…) that fits the plots.
These functions can be used to give a simple model for how noise accumulates at a global level for the GHZ circuit. How much worse is this than the single-qubit gates
situation?
Problem 4.5 – Running this on an actual quantum computer
So far you’ve been running all this on a simulated version of the 5-qubit ibmq_essex device (which has been retired). Now let’s actually run it on a real quantum
computer — how exciting!
If you look at the available systems on https://quantum-computing.ibm.com/services/resources, you will see that there are a number of devices ( ibmq_belem ,
ibm_perth , ibm_lagos , ibm_nairobi , etc) with varying numbers of qubits, and with different levels of busy-ness (some have more jobs in the queue than
# Returns:
# – the IBMQJob object corresponding to the circuit (see https://qiskit.org/documentation/stubs/qiskit.providers.ibmq.job.IBMQJob.html#qis
##########
def benchmark_circuit(r, k, shots, device):
circ = QuantumCircuit(5, r)
### WRITE CODE TO GENERATE C_r and its reversal k times ####################
## You need to choose your subset of qubits carefully!
### END CODE BLOCK ###################
### WRITE CODE TO MEASURE AND COMPUTE P_RK #####################
# measure r of the qubits , and store it in the r classical registers
#for example, if r = 3, circ.measure([1,3,4],[0,1,2]) would mean measuring qubits 1,3,4
# <--- WRITE MEASUREMENT CODE HERE
#this will break down the circuit into gates that can be implemented on the chip
compiled_circuit = transpile(circ,device,optimization_level=0)
job = device.run(compiled_circuit,shots=shots)
return job
#### WRITE CODE TO CALL benchmark_circuit, retrieve_job_results AND PLOT p_rk versus k #################
#### END CODE BLOCK ############################################################
r = 2, 3, 4, 5 f(k)
If you haven't already, please add your IBM Quantum login email to the spreadsheet
https://docs.google.com/spreadsheets/d/1tB27ZyoDozMzBN9Ya1-bEx6f_pa-VfibDlXVrkA9XCQ/edit?usp=sharing
so that you can get the special education access to their machines (your jobs are processed faster).
For this problem, you will need to run this on the IBM Quantum Lab. The next two commands will show whether you have the special education access.
If you don't see something like
the printed list, then e-mail Prof. Yuen and he will add you as a collaborator.
The next code will get the machines that are available to you:
Next, pick a machine to use. In our simulated code above, we choose ibmq_essex , but you have to choose some other device (because essex has been retired). You
should check https://quantum-computing.ibm.com/services/resources to see which ones have the shortest line.
Now that you’ve loaded a device, now you can run the same code ( benchmark_qubit and benchmark_circuit ), except by passing real_device to the
functions this time your circuits will be run on some qubits somewhere in upstate New York! Your goal in this part of the problem is to do the same benchmarking, and
compare the results with simulation. Since time on the IBM computers are limited, we only ask you to plot versus for just one of the qubits, and to only plot
versus for .
Unlike with the simulation code, your circuits will be queued after you call benchmark_qubit or benchmark_circuit . Each call will return a job that takes time to
run (usually a few minutes per job). A hundred circuits will likely take a whole day to finish. (So be sure to test your code first in simulation before submitting jobs to the
IBM quantum machines!)
To avoid situations where you lose the result of your jobs, in this part you should structure your code as follows:
�. Call the benchmark_qubit and benchmark_circuit code all at once, and save the job IDs to a file.
�. Periodically check the status of your jobs (either programmatically or using the “Jobs” control panel in IBM Quantum Lab).
�. Once you see they’re done, retrieve the results as before.
Below we provide some helper functions to save jobs to a file.
In [ ]: IBMQ.load_account()
IBMQ.providers()
In [ ]: provider = IBMQ.get_provider(hub=’ibm-q-education’)
provider.backends()
In [ ]: real_device = provider.get_backend(‘ibmq_PICK_DEVICE_HERE’)
real_device.status()
In [ ]: ##########
# This function takes as input
# – list_of_jobs: self-explanatory
Next, write code for Step 1. You should use save_jobs_to_file to store your work.
Warning: If you execute it multiple times, it will resubmit the same jobs! We suggest testing it out with one or two circuit jobs first before scaling up.
Next, write some code to load the jobs ids and check on their status. You can run this block even if you’ve re-opened the Jupyter notebook.
# – filename: file to store the job_ids
##########
def save_jobs_to_file(list_of_jobs,filename):
#open the file
with open(filename,’w’) as f:
for job in list_of_jobs:
f.write(job.job_id())
f.write(‘\n’)
##########
# This function takes as input
# – name_of_device: this is the name of the device you submitted the jobs to, such as `ibmq_belem` or `ibm_nairobi`, etc.
# – filename: file that has all the job_ids
# Returns: a list_of_jobs
##########
def load_jobs_from_file(name_of_device,filename):
IBMQ.load_account()
provider = IBMQ.get_provider(hub=’ibm-q-education’)
device = provider.get_backend(name_of_device)
list_of_jobs = []
with open(filename,’r’) as f:
job_ids = f.readlines()
for job_id in job_ids:
job = device.retrieve_job(job_id.strip())
list_of_jobs.append(job)
return list_of_jobs
##########
# This function takes as input
# – list_of_jobs: self-explanatory
##########
def print_job_statuses(list_of_jobs):
for job in list_of_jobs:
print(“id: “,job.job_id(),” status: “, job.status())
In [8]: ### INSERT YOUR STEP 1 CODE HERE ######################
###############################################
In [ ]: ### INSERT YOUR STEP 2 CODE HERE ######################
Finally, once you see all the job statuses are done, retrieve the job results. Remember that retrieve_job_results takes in an argument called blocking , which
you should set to False here.
How do the results from the actual hardware compare with simulation? Quantify the differences, if any.
Problem 5: Quantum Tug-of-War!
In this part of the problem set you will write a strategy for a quantum video game. See the accompanying Jupyter notebook QTugofwar.ipynb for details.
#list_of_jobs = load_jobs_from_file(‘name_of_device’,’filename.txt’)
#print_job_statuses(list_of_jobs)
###############################################
In [7]: ### INSERT YOUR STEP 3 CODE HERE ######################
###############################################
In [ ]: ## Solution