Welcome to the IBM Quantum Challenge: Spring 2023!
This iteration of the IBM Quantum Challenge is dedicated to dynamic circuits. You're currently in the first lab of the challenge. As work through the labs, you will be required to complete some exercises. In this introductory section, we will give you a brief overview of how to complete the exercises. Even if you have participated in previous challenges, you may want to review this material.
All the labs will contain a mixture of tutorial content, pre-written code blocks, and exercise code blocks which will require you to fill in your own Qiskit code. The code required to complete the exercise should be typed under the line which has the "### Your code goes here ###" comment.
If you've never been part of a Challenge before, it's important to note that you should run every code cell, even if you didn't write any new code within. This makes sure that when you submit your answers by running the grader, everything is up to date.
In the cell below, we construct a circuit with a single qubit and a single classical bit. Your first task is to perform a Hadamard gate on the qubit and then measure it, storing the result in the classical bit. We've filled in the code for you, all you need to do is remove the #
at the start of both lines.
%env QXToken=7af7457aa4bd86a0403d2f34db141b3230cb7cfdf3124a009335cbcbe9ea273b918b9e5f9ac55d16e5a457d26994b81227e10a05f121e59534972c54968ab396
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
qr = QuantumRegister(1) cr = ClassicalRegister(1) qc = QuantumCircuit(qr, cr)
qc.h(qr[0])
qc.measure(qr[0], cr[0])
qc.draw("mpl")
Nice job! After each exercise we'll need to figure out if what you wrote is correct. To do this, you simply run a grader cell.
Let's try it out. In the next cell, we ask you to set an integer value for the variable answer_0
. If the integer is between 1 and 5, the grader will give back a message congratulating you for finding the right answer. If you enter any other number, you will get the opposite message.
Set the answer to whatever you'd like, then run the grader cell to see your results. You can try this a few times with different numbers for fun. answer_0: int answer_0 = 1
from qc_grader.challenges.spring_2023 import grade_ex1a
grade_ex1a(answer_0)
Now you're all set to start this challenge. Good luck and have fun!
According to the Qiskit textbook,
"A quantum circuit is a computational routine consisting of coherent quantum operations on qubits ... It is an ordered sequence of quantum gates, measurements and resets, all of which may be conditioned on and use data from the real-time classical computation."
You might not be familiar with the last part of the definition, but it has everything to do with dynamic circuits. Usually when we talk about quantum circuits, we're referring to static quantum circuits, even if we don't explicitly say it. So what are dynamic circuits?
Dynamic circuits are quantum circuits that contain mid-circuit measurements where the results of those measurements are used to condition quantum gates later in the circuit. The ability to condition future quantum operations on the classical measurement results is known as classical feedforward.
Why do we need classical feedforward? An example may help: Consider the conditional reset operation, which may sound familiar if you've ever used the reset
operation in Qiskit. While the reset operation is a non-unitary operation that resets the qubit to the zero state no matter the initial state, the conditional reset operation is performed on an already measured qubit to reset it to zero by applying a bit-flip X gate only if the qubit is in the one state, as determined from its measured value. The conditional reset can be a faster and less error-prone way to reset a qubit if it has already been measured.
In Qiskit, the syntax for programming dynamic circuits has gone through several iterations, and full support has not yet been implemented. Currently, the only way to access the full capabilities of dynamic circuits is to submit programs written in OpenQASM 3. Nevertheless, in this challenge we will be working with what is currently available in Qiskit.
An earlier version of Qiskit introduced the c_if()
instruction, but this syntax will be deprecated in favor of the more flexible if_test()
method of QuantumCircuit, which is the method we'll be mostly using in this challenge.
To get you started, we're going to review a simple example where we'll use this function. We'll build a circuit that demonstrates the conditional reset operation by taking the following steps:
- Initialize a circuit with 1 qubit and 2 classical bits. (
$q_{0}$ ,$b_{0}$ and$b_{1}$ ) - Apply a Hadamard gate to
$q_{0}$ . - Measure that qubit and save the result in
$b_{0}$ . - Begin an
if_test
block conditioned on$b_{0}$ being equal to 1. - In the
if_test
block, specify the operation to do if the condition is met, in this case, flipping$q_{0}$ to 0 state. - Measure
$q_0$ again into$b_{1}$ to check that we always get 0. from qiskit import QuantumCircuit from qiskit.circuit import QuantumRegister, ClassicalRegister
qr = QuantumRegister(1) cr = ClassicalRegister(2) qc = QuantumCircuit(qr, cr)
(q0,) = qr b0, b1 = cr
qc.h(q0)
qc.measure(q0, b0)
with qc.if_test((b0, 1)): # if the condition is satisfied (b0 == 1), then flip the bit back to 0 qc.x(q0)
qc.measure(q0, b1)
qc.draw(output="mpl", idle_wires=False)
Now that our circuit is built, let's run it several times to see if we always get the expected output. The first measurement could be either 0 or 1, but the second measurement should always be 0. from qiskit_aer import AerSimulator
backend_sim = AerSimulator()
reset_sim_job = backend_sim.run(qc)
reset_sim_result = reset_sim_job.result()
reset_sim_counts = reset_sim_result.get_counts()
print(f"Counts: {reset_sim_counts}")
As expected, the first bit is sometimes 0 and sometimes 1, but the second bit is always 0 (recall that Qiskit uses little-endian bit-ordering, so that the right-most bit is the first bit and the left-most bit is the last bit). from qiskit.visualization import *
plot_histogram(reset_sim_counts) Now you're ready to build your first dynamic circuit!
Let's level things up. Your first assignment will be to design a two qubit circuit. In this case, the aim will be to act differently on
To make the value of if_test()
documentation.
from qiskit import QuantumCircuit
from qiskit.circuit import QuantumRegister, ClassicalRegister
qr = QuantumRegister(2) cr = ClassicalRegister(2) qc = QuantumCircuit(qr, cr)
q0, q1 = qr b0, b1 = cr
qc.h(q0) qc.measure(q0, b0)
with qc.if_test((b0, 1))as else_: qc.h(q1) with else_: qc.x(q1)
qc.measure(q1, b1)
qc.draw(output="mpl", idle_wires=False)
Optionally, you can run next cell to check if your circuit behaves as expected.
Tip: Think of the possible outcomes of the circuit before running it. backend_sim = AerSimulator()
job_1 = backend_sim.run(qc) result_1 = job_1.result() counts_1 = result_1.get_counts()
print(f"Counts: {counts_1}")
from qc_grader.challenges.spring_2023 import grade_ex1b
grade_ex1b(qc)
Sometimes the outcome of a process is random and the result you get isn't what you wanted. What can you do in this case? Well, you can try again! As long as there is some nonzero probability of your desired outcome, repeating the process is guaranteed to return the result you wanted, eventually. Often, only a few repetitions will be needed.
In this section, we will use the repeat-until-success idea to build an
The way the construction works is that we will build a circuit that acts on 3 qubits. One of the qubits is the target qubit and our goal is to perform the
Your first task of this section will be to create a function that returns that circuit. Here there's an image of how the circuit should look like:
To make things easier for you, we'll set up the "base" circuit for you with the required elements. The functions we'll write in this lab will take a circuit as input and modify it in place. Whenever we need a fresh circuit, we'll just make a copy of the base circuit. controls = QuantumRegister(2, name="control") target = QuantumRegister(1, name="target")mid_measure = ClassicalRegister(2, name="mid") final_measure = ClassicalRegister(1, name="final")
base = QuantumCircuit(controls, target, mid_measure, final_measure)
In the next cell, fill in the trial
function so that it constructs the circuit by taking the following steps:
- Apply a Hadamard gate to each qubit of the control register as well as the target qubit.
- Apply the Toffoli (controlled-controlled-not) gate between the control register and the target qubit. This can be achieved using either the
ccx
method of QuantumCircuit, or by importing and usingCCXGate
fromqiskit.circuit.library
. - Apply an
$S$ gate to the target qubit. - Apply another Toffoli gate, with the same controls and target as Step 2.
- Again, apply Hadamard to the control and target registers.
- Measure the control register into the classical register. from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister from qiskit.circuit.library import CCXGate
qc = base.copy_empty_like()
def trial(circuit: QuantumCircuit, target: QuantumRegister, controls: QuantumRegister, measures: ClassicalRegister): """Probabilistically perform Rx(theta) on the target, where cos(theta) = 3/5.""" circuit.h(target) circuit.h(controls) circuit.append(CCXGate(), [controls[0], controls[1], target]) circuit.s(target) circuit.append(CCXGate(), [controls[0], controls[1], target]) circuit.h(target) circuit.h(controls) circuit.measure(controls, measures)
trial(qc, target, controls, mid_measure)
qc.draw("mpl", cregbundle=False)
from qc_grader.challenges.spring_2023 import grade_ex1c
grade_ex1c(qc)
Your next task will be to check the measurements. If both measurements of the control bits return reset_controls
function so that it modifies the circuit by taking the following steps:
-
If the first bit of the
measures
register is equal to 1, apply an$X$ gate to the first control qubit. -
If the second bit of the
measures
register is equal to 1, apply an$X$ gate to the second control qubit. def reset_controls(circuit: QuantumCircuit, controls: QuantumRegister, measures: ClassicalRegister): """Reset the control qubits if they are in |1>."""with circuit.if_test((measures[0], True)): circuit.x(controls[0]) # Apply X gate to the first control qubit if the first bit of measures is 1
with circuit.if_test((measures[1], True)): circuit.x(controls[1]) # Apply X gate to the second control qubit if the second bit of measures is 1
qc = base.copy_empty_like() trial(qc, target, controls, mid_measure) reset_controls(qc, controls, mid_measure) qc.measure(controls, mid_measure) qc.draw("mpl", cregbundle=False)
from qc_grader.challenges.spring_2023 import grade_ex1d
grade_ex1d(qc) Now all that is left to do is repeat the execution of the circuit if the conditions weren't satisfied. In the final exercise, we will need to work around two issues in our current support for dynamic circuits.
The first issue is that Qiskit currently does not support performing any arithmetic or logical operations on classical bits. In particular, it does not support checking that a classical register does not have a certain value. In our situation, we need to repeat the trial only if the mid-circuit measurement was not the bitstring 00
. To work around this issue, we will create an if statement conditioned on the measurement being equal to 00
, pass an empty block, and then use the else branch to perform the logic we want for the case that the measurement is not 00
.
The second issue is that our hardware currently does not support loops. Therefore, we cannot execute a true repeat-until-success loop. We will work around this issue by simply repeating our trial circuit a fixed number of times.
In the code cell below, fill in the else
block with the logic that should be performed in case the syndrome measurement indicates we need to repeat the trial:
- Reset the target qubit to the zero state. Remember, we already know that it is in the 1 state.
- Call the
trial
function on the circuit again.
max_trials = 2
circuit = base.copy_empty_like()
trial(circuit, target, controls, mid_measure)
for _ in range(max_trials - 1):
reset_controls(circuit, controls, mid_measure)
with circuit.if_test((mid_measure, 0b00)) as else_:
# This is the success path, but Qiskit can't directly
# represent a negative condition yet, so we have an
# empty true
block in order to use the else
branch.
pass
with else_:
# Reset the target qubit to the zero state
circuit.reset(target)
# Call the trial function on the circuit again
trial(circuit, target, controls, mid_measure)
circuit.measure(controls, mid_measure)
circuit.measure(target, final_measure)
circuit.draw("mpl", cregbundle=False)
from qc_grader.challenges.spring_2023 import grade_ex1e
grade_ex1e(circuit) Let's run the circuit on a simulator. sim = AerSimulator() job = sim.run(circuit, shots=1000) result = job.result() counts = result.get_counts()
plot_histogram(counts)
A successful result is one in which the measurements on the two controls end in the 00
state. If you're having trouble, we encourage you to post about your results on the Discord Channel, and try to understand in collaboration with other participants what your results mean and why you are getting them.
Let's try running the circuit we made on real hardware! For this, we'll be using the 27-qubit Peekskill device, which has been tuned specifically for dynamic circuits. But please remember, running on real hardware takes time, and sometimes has errors. Please do not run these cells over and over, since that will cause a backup for all the other Challenge participants. from qiskit_ibm_provider import IBMProvider
provider = IBMProvider() hub = "YOUR_HUB" group = "YOUR_GROUP" project = "YOUR_PROJECT"
backend_name = "ibm_peekskill" backend = provider.get_backend(backend_name, instance=f"{hub}/{group}/{project}") from qiskit import transpile
qc_transpiled = transpile(circuit, backend) job = backend.run(qc_transpiled, shots=1000, dynamic=True)
counts = job.result().get_counts()
plot_histogram(counts)
You've made it to the end of the first lab! Now that you know more about dynamic circuits, it is time to move onto the next lab and start learning about some applications and more advanced properties. Good luck!