Cette page n'a pas encore été traduite. Vous voyez la version originale en anglais.
The Deutsch-Jozsa algorithm
For this Qiskit in Classrooms module, students must have a working Python environment with the following packages installed:
qiskitv2.1.0 or newerqiskit-ibm-runtimev0.40.1 or newerqiskit-aerv0.17.0 or newerqiskit.visualizationnumpypylatexenc
To set up and install the packages above, see the Install Qiskit guide. In order to run jobs on real quantum computers, students will need to set up an account with IBM Quantum® by following the steps in the Set up your IBM Cloud account guide.
This module was tested and used four seconds of QPU time. This is an estimate only. Your actual usage may vary.
# Uncomment and modify this line as needed to install dependencies
#!pip install 'qiskit>=2.1.0' 'qiskit-ibm-runtime>=0.40.1' 'qiskit-aer>=0.17.0' 'numpy' 'pylatexenc'
Watch the module walkthrough by Dr. Katie McCormick below, or click here to watch it on YouTube.
Intro
In the early 1980's, quantum physicists and computer scientists had a vague notion that quantum mechanics could be harnessed to make computations that were far more powerful than classical computers can make. Their reasoning was this: it's difficult for a classical computer to simulate quantum systems, but a quantum computer should be able to do it more efficiently. And if a quantum computer could simulate quantum systems more efficiently, perhaps there were other tasks that it could perform more efficiently than a classical computer.
The logic was sound, but the details remained to be worked out. This began in 1985, when David Deutsch described the first "universal quantum computer." In this same paper, he provided the first example problem for which a quantum computer could solve something more efficiently than a classical computer could. This first toy example is now known as "Deutsch's algorithm." The improvement in Deutsch's algorithm was modest, but Deutsch worked with Richard Jozsa a few years later to further widen the gap between classical and quantum computers.
These algorithms — Deutsch's, and the Deutsch-Jozsa extension — are not particularly useful, but they are still really important for a few reasons:
- Historically, they were some of the first quantum algorithms that were demonstrated to beat their classical counterparts. Understanding them can help us understand how the community's thinking on quantum computing has evolved over time.
- They can help us understand some aspects of the answer to a surprisingly subtle question: What gives quantum computing its power? Sometimes, quantum computers are compared to giant, exponentially-scaling parallel processors. But this isn't quite right. While a piece of the answer to this question lies in so-called "quantum parallelism," extracting as much information as possible in a single run is a subtle art. The Deutsch and Deutsch-Jozsa algorithms show how this can be done.
In this module, we'll learn about Deutsch's algorithm, the Deutsch-Jozsa algorithm, and what they teach us about the power of quantum computing.
Quantum parallelism and its limits
Part of the power of quantum computing is derived from "quantum parallelism." which is essentially the ability to perform operations on multiple inputs at the same time, since the qubit input states could be in a superposition of multiple classically allowed states. HOWEVER, while a quantum circuit might be able to evaluate multiple input states at once, extracting all of that information in one go is impossible.
To see what I mean here, let's say we have a bit, and some function applied to that bit, . There are four possible binary functions taking a single bit to another single bit:
| 0 | 0 | 0 | 1 | 1 |
| 1 | 0 | 1 | 0 | 1 |
We'd like to find out which of these functions (1-4) our is. Classically, we would need to run the function twice — once for , once for . But let's see if we can do better with a quantum circuit. We can learn about the function with the following gate:
Here, the gate computes , where is the state of qubit 0, and applies that to qubit 1. So, the resulting state, , simply becomes when . This contains all the information we need to know the function : qubit 0 tells us what is, and qubit 1 tells us what is. So, if we initialize , then the final state of both qubits will be: . But how do we access that information?
2.1. Try it on Qiskit:
Using Qiskit we'll randomly select one of the four possible functions above and run the circuit. Then your task is to use the measurements of the quantum circuit to learn the function in as few runs as possible.
In this first experiment and throughout the module, we will use a framework for quantum computing known as "Qiskit patterns", which breaks workflows into the following steps:
- Step 1: Map classical inputs to a quantum problem
- Step 2: Optimize problem for quantum execution
- Step 3: Execute using Qiskit Runtime Primitives
- Step 4: Post-processing and classical analysis
Let's start by loading some necessary packages, including the Qiskit Runtime primitives. We will also select the least busy quantum computer available to us.
There is code below for saving your credentials upon first use. Be sure to delete this information from the notebook after saving it to your environment, so that your credentials are not accidentally shared when you share the notebook. See Set up your IBM Cloud account and Initialize the service in an untrusted environment for more guidance.
# Load the Qiskit Runtime service
from qiskit_ibm_runtime import QiskitRuntimeService
# Load the Runtime primitive and session
from qiskit_ibm_runtime import SamplerV2 as Sampler
# Syntax for first saving your token. Delete these lines after saving your credentials.
# QiskitRuntimeService.save_account(channel='ibm_quantum_platform', instance = '<YOUR_IBM_INSTANCE_CRN>', token='<YOUR_API_KEY>', overwrite=True, set_as_default=True)
# service = QiskitRuntimeService(channel='ibm_quantum_platform')
# Load saved credentials
service = QiskitRuntimeService()
# Use the least busy backend, or uncomment the loading of a specific backend like "ibm_brisbane".
# backend = service.least_busy(operational=True, simulator=False, min_num_qubits = 127)
backend = service.backend("ibm_brisbane")
print(backend.name)
sampler = Sampler(mode=backend)
ibm_brisbane
The cell below will allow you to switch between using the simulator or real hardware throughout the notebook. We recommend running it now:
# Load the backend sampler
from qiskit.primitives import BackendSamplerV2
# Load the Aer simulator and generate a noise model based on the currently-selected backend.
from qiskit_aer import AerSimulator
from qiskit_aer.noise import NoiseModel
# Alternatively, load a fake backend with generic properties and define a simulator.
noise_model = NoiseModel.from_backend(backend)
# Define a simulator using Aer, and use it in Sampler.
backend_sim = AerSimulator(noise_model=noise_model)
sampler_sim = BackendSamplerV2(backend=backend_sim)
# You could also define a simulator-based sampler using a generic backend:
# backend_gen = GenericBackendV2(num_qubits=18)
# sampler_gen = BackendSamplerV2(backend=backend_gen)
Now that we've loaded the necessary packages, we can proceed with the Qiskit patterns workflow. In the mapping step below, we first make function that selects among the four possible functions taking a single bit to another single bit.
# Step 1: Map
from qiskit import QuantumCircuit
qc = QuantumCircuit(2)
def twobit_function(case: int):
"""
Generate a valid two-bit function as a `QuantumCircuit`.
"""
if case not in [1, 2, 3, 4]:
raise ValueError("`case` must be 1, 2, 3, or 4.")
f = QuantumCircuit(2)
if case in [2, 3]:
f.cx(0, 1)
if case in [3, 4]:
f.x(1)
return f
# first, convert oracle circuit (above) to a single gate for drawing purposes. otherwise, the circuit is too large to display
# blackbox = twobit_function(2).to_gate() # you may edit the number inside "twobit_function()" to select among the four valid functions
# blackbox.label = "$U_f$"
qc.h(0)
qc.barrier()
qc.compose(twobit_function(2), inplace=True)
qc.measure_all()
qc.draw("mpl")
In the above circuit, the Hadamard gate "H" takes qubit 0, which is initially in the state , to the superposition state . Then, evaluates the function and applies that to qubit 1.
Next we need to optimize and transpile the circuit to be run on the quantum computer:
# Step 2: Transpile
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
target = backend.target
pm = generate_preset_pass_manager(target=target, optimization_level=3)
qc_isa = pm.run(qc)
Finally, we execute our transpiled circuit on the quantum computer and visualize our results:
# Step 3: Run the job on a real quantum computer
job = sampler.run([qc_isa], shots=1)
# job = sampler_sim.run([qc_isa],shots=1) # uncomment this line to run on simulator instead
res = job.result()
counts = res[0].data.meas.get_counts()
# Step 4: Visualize and analyze results
## Analysis
from qiskit.visualization import plot_histogram
plot_histogram(counts)
The above is a histogram of our results. Depending on the number of shots you chose to run the circuit in step 3 above, you could see one or two bars, representing the measured states of the two qubits in each shot. As always with Qiskit and in this notebook, we use "little endian" notation, meaning the states of qubits 0 through n are written in ascending order from right to left, so qubit 0 is always farthest right.
So, because qubit 0 was in a superposition state, the circuit evaluated the function for both and at the same time — something classical computers cannot do! But the catch comes when we want to learn about the function — when we measure the qubits, we collapse their state. If you select "shots = 1" to only run the circuit once, you will only see one bar in the histogram above, and your information about the function will be incomplete.
Check your understanding
Read the question(s) below, think about your answer, then click the triangle to reveal the solution.
How many times must we run the above algorithm to learn the function ? Is this any better than the classical case? Would you rather have a classical or quantum computer to solve this problem?
Answer:
Since the measurement will collapse the superposition and return only one value, we need to run the circuit at least twice to return both outputs of the function and . Best case, this performs as well as the classical case, where we compute both and in the first two queries. But there's a chance that we'll need to run it more than two times, since the final measurement is probabilistic and might return the same value the first two times. I would rather have a classical computer in this case.
So, while quantum parallelism can be powerful when used in the right way, it is not correct to say that a quantum computer works just like a massive, classical parallel processor. The act of measurement collapses the quantum states, so we can only ever access a single output of the computation.
Deutsch's algorithm
While quantum parallelism alone doesn't give us an advantage over classical computers, we can pair this with another quantum phenomena, interference, to achieve a speed-up. The algorithm now known as "Deutsch's algorithm" is the first example of an algorithm that accomplishes this.
The problem
Here was the problem:
Given an input bit, , and an input function , determine whether the function is balanced or constant. That is, if it's balanced, then the output of the function is 0 half the time and 1 the other half the time. If it's constant, then the output of the function is either always 0 or always 1. Recall the table of four possible functions taking a single bit to another a single bit:
| 0 | 0 | 0 | 1 | 1 |
| 1 | 0 | 1 | 0 | 1 |
The first and the last functions, and , are constant, while the middle two functions, and , are balanced.
The algorithm
The way Deutsch approached this problem was through the "query model." In the query model, the input function ( above) is contained in a "black box" — we don't have direct access to its contents, but we can query the black box and it will give us the output of the function. We sometimes say that an "oracle" provides this information. See Lesson 1: Quantum Query Algorithms of the Fundamentals of Quantum Algorithms course for more on the query model.
To determine whether a quantum algorithm is more efficient than a classical algorithm in the query model, we can simply compare the number of queries we need to make of the black box in each case. In the classical case, in order to know if the function contained in the black box were balanced or constant, we would need to query the box two times to get both and .
In Deutsch's quantum algorithm, though, he found a way to get the information with only one query! He made one adjustment to the "quantum parallelism" circuit above, so that he prepared a superposition state on both qubits, instead of only on qubit 0. Then the two outputs of the function, and interfered to return 0 if they were either both 0 or both 1 (the function was constant), and returned 1 if they were different (the function was balanced). In this way, Deutsch could differentiate between a constant and a balanced function with a single query.
Here's a circuit diagram of Deutsch's algorithm:

To understand how this algorithm works, let's look at the quantum states of the qubits at the three points noted on the diagram above. Try to work out the states for yourself before clicking to view the answers:
Check your understanding
Read the questions below, think about your answers, then click the triangles to reveal the solutions.
What is the state ?
Answer:
Applying a Hadamard transforms the state to and the state to . So, the full state becomes:
What is the state ?
Answer:
Before we apply , remember what it does. It will change the state of qubit 1 based on the state of qubit 0. So, it makes sense to factor the state of qubit 0 out: . Then, if , the two terms will transform in the same way and the relative sign between the two terms remains positive, but if , then that means the second term will pick up a minus sign relative to the first term, changing the state of qubit 0 from to . So:
What is the state ?
Answer:
Now, the state of qubit 0 is either or , depending on the function. Applying the Hadamard will yield either