Hello! In our last lesson, we unpacked the theory behind the Deutsch-Jozsa algorithm. We saw how it uses quantum parallelism and interference to determine if a function is constant or balanced in a single query—a task that can be exponentially harder for a classical computer.
Today, we'll transition from theory to practice. This lesson is all about implementation. Our goal is to implement the Deutsch-Jozsa algorithm in Qiskit for a given oracle. You will learn how to construct the quantum circuit that forms the algorithm's backbone, how to build different oracles representing constant and balanced functions, and how to run the full algorithm and interpret its results.
1. The Deutsch-Jozsa Circuit Structure in Qiskit
As we discussed previously, the algorithm has a distinct structure that we can think of as a "Hadamard sandwich." It prepares a superposition, queries the oracle, and then uses interference to get the answer. Let's outline the steps to build this in Qiskit for an -bit function.
The circuit requires qubits for the input and one additional qubit, called an ancilla, for the output. So, we'll need qubits in total.
The general process is:
- Initialization: Start with qubits in the state.
- Ancilla Preparation: Apply an X gate to the ancilla qubit to flip it to .
- Superposition: Apply Hadamard (H) gates to all qubits. This puts the input qubits into an equal superposition of all possible states, and the ancilla qubit into the state .
- Oracle Query: Apply the quantum oracle, .
- Interference: Apply H gates to the first input qubits.
- Measurement: Measure the first input qubits.

Now, let's translate this into a Qiskit circuit. The IBM Quantum Learning resource provides a very clear template for this.
This reading provides the full Qiskit implementation of the Deutsch-Jozsa algorithm.
Focus on the section "The Deutsch-Jozsa algorithm." Study the general circuit diagram and then examine the Qiskit code cell that begins with qc_dj = QuantumCircuit(n + 1, n). This cell builds the exact structure we just described, leaving a space for the blackbox (oracle).
The key insight is that this structure is always the same. The only part that changes depending on the specific problem is the oracle, .
2. Building the Oracle,
The "given oracle" in our learning outcome is the quantum circuit that implements the function . The oracle must be a unitary operation that acts on the input and ancilla qubits. For a function , the oracle performs the mapping:
where is the -qubit input register, is the single ancilla qubit, and denotes addition modulo 2 (a classical XOR operation).
Let's see how to build oracles for both constant and balanced functions.
Constant Oracle
A constant oracle is straightforward.
- For for all : The function is . The oracle does nothing. It's an identity operation.
- For for all : The function is . The oracle applies an X (NOT) gate to the ancilla qubit, regardless of the input .
Balanced Oracle
A balanced oracle is more interesting. There are many ways to construct one. A common example is a function that is 1 for a specific input bitstring and 0 for all others. A more structured example is a parity function, where is 1 if the input string has an odd number of 1s, and 0 otherwise.
This can be implemented with a cascade of CNOT gates, where each input qubit acts as a control for a CNOT targeting the ancilla. Since a CNOT is equivalent to a controlled-XOR, this sequence effectively calculates the XOR sum (parity) of the input bits onto the ancilla.
The following resource provides excellent code examples for building various oracles.
Comments on Black Box Implementation
This text shows practical Qiskit code for implementing different types of oracle functions.
Read the section "2.3 Comments on Black Box Implementation." Pay close attention to the code blocks for: Implementing a constant function: Notice how simple it is (just an X gate on the ancilla for f(x)=1). Implementation of parity function: See how a chain of CNOT gates creates a balanced oracle.
3. Assembling and Running the Full Algorithm
Now we have the two main components: the "Hadamard sandwich" circuit and the oracle. Let's put them together. The sentdex video below offers a very hands-on walkthrough of this entire process, from defining the oracles to running the final circuit.
Deutsch Jozsa Algorithm - Quantum Computer Programming w/ Qiskit p.3
This video provides a step-by-step guide to coding the Deutsch-Jozsa algorithm in Qiskit.
First, watch the section on defining the oracles (here). This reinforces what you just read, showing how to wrap the oracle logic in a Python function. Next, watch the full circuit construction and execution (here). Pay attention to how the balanced_black_box and constant_black_box are placed within the Hadamard gates and how the final measurement is interpreted. Note his emphasis on needing only a single shot.
Let's consolidate what you've learned into a complete example. We'll implement the algorithm for input qubits.
Step 1: Define the Oracles
First, we create Python functions that return QuantumCircuit objects for our oracles.
import numpy as np
from qiskit import QuantumCircuit, Aer, transpile
from qiskit.visualization import plot_histogram
# For n=3 input qubits, the circuit has 3+1 = 4 qubits
n = 3
# Oracle for a CONSTANT function, f(x) = 1
# This oracle flips the ancilla qubit regardless of the input.
constant_oracle = QuantumCircuit(n + 1)
constant_oracle.x(n) # Apply X gate to the ancilla qubit (at index n)
constant_oracle.name = "Constant f(x)=1"
# Oracle for a BALANCED function (parity)
# f(x) = 1 if the input has an odd number of 1s
balanced_oracle = QuantumCircuit(n + 1)
# Apply CNOT gates from each input qubit to the ancilla qubit
for qubit in range(n):
balanced_oracle.cx(qubit, n)
balanced_oracle.name = "Balanced (Parity)"
Step 2: Build the Main Deutsch-Jozsa Circuit
Now, we create a function that takes an oracle and wraps it in the Deutsch-Jozsa algorithm structure.
def dj_circuit(oracle):
n = oracle.num_qubits - 1
# Create the circuit with n+1 quantum bits and n classical bits
qc = QuantumCircuit(n + 1, n)
# Prepare the ancilla in state |->
qc.x(n)
qc.h(n)
# Apply Hadamard to all input qubits
for qubit in range(n):
qc.h(qubit)
# Add a barrier for visualization
qc.barrier()
# Append the oracle
qc.append(oracle, range(n + 1))
# Add another barrier
qc.barrier()
# Apply Hadamard to input qubits again
for qubit in range(n):
qc.h(qubit)
# Measure the input qubits
qc.measure(range(n), range(n))
return qc
Step 3: Run with the Constant Oracle
Let's build the circuit with the constant oracle and see the result.
# Create the full circuit
full_circuit_constant = dj_circuit(constant_oracle)
print("Circuit with Constant Oracle:")
display(full_circuit_constant.draw('mpl'))
# Simulate the circuit
simulator = Aer.get_backend('aer_simulator')
# We only need one shot to get the answer!
result = simulator.run(full_circuit_constant, shots=1, memory=True).result()
counts = result.get_counts()
print("\nResult from one shot:", counts)
plot_histogram(counts)
You should see that the measurement result is '000' with 100% certainty.
Step 4: Run with the Balanced Oracle
Now, let's swap in the balanced oracle.
# Create the full circuit
full_circuit_balanced = dj_circuit(balanced_oracle)
print("Circuit with Balanced Oracle:")
display(full_circuit_balanced.draw('mpl'))
# Simulate the circuit
result = simulator.run(full_circuit_balanced, shots=1, memory=True).result()
counts = result.get_counts()
print("\nResult from one shot:", counts)
plot_histogram(counts)
This time, the result should be anything but '000'. For the parity oracle, it willdeterministically be '111'.
The final video clip shows the same procedure for a randomly generated Deutsch-Jozsa function.
Quantum vs Classical: Deutsch & Deutsch-Jozsa Algorithms Explained
This video shows the Qiskit implementation and results for a randomly generated Deutsch-Jozsa function.
Watch the final segment on the Deutsch-Jozsa algorithm (here). Notice how their code creates a random oracle and then inserts it into the same circuit structure we've been using. The final measurement confirms if the random function was constant or balanced.
Conclusion
In this lesson, you successfully translated the abstract concepts of the Deutsch-Jozsa algorithm into a working quantum program using Qiskit. You have seen how the algorithm's power comes from a fixed quantum circuit structure that cleverly uses interference, with the problem-specific logic entirely contained within a swappable quantum oracle.
Key Takeaways:
- The Deutsch-Jozsa algorithm is implemented in Qiskit using a "Hadamard sandwich" structure around an oracle, .
- Implementing the algorithm "for a given oracle" means building a
QuantumCircuitthat performs the mapping . - Constant oracles are simple (identity or an X gate on the ancilla), while balanced oracles can be built with controlled gates like CNOTs to reflect the function's logic.
- By running the circuit just once, we can distinguish between a constant and a balanced function: a measurement of
00...0means the function is constant, and any other result means it's balanced.
Preview of the Next Lesson:
The Deutsch-Jozsa algorithm, while a fantastic proof of concept, solves a rather contrived problem. In our next lesson, we will explore the Bernstein-Vazirani algorithm. You'll find it uses a nearly identical circuit structure, but it solves a more interesting problem: efficiently finding a hidden binary string encoded within an oracle.