Quantum vs classical implementation of Deutsch’s Algorithm in Qiskit: same answer, half the queries.
The previous post covered the theory: four Boolean functions, the reversible oracle Uf, the phase-kickback derivation, and the single-query measurement result. This post puts it into running code. We implement the classical two-query approach and the quantum one-query approach side by side in Qiskit so the advantage is visible, not just promised.
Given a black-box function f: {0,1} → {0,1}, determine whether it is constant (f(0) = f(1)) or balanced (f(0) ≠ f(1)).
| Approach | Oracle queries required | Strategy |
|---|---|---|
| Classical | 2 | Evaluate f(0), then f(1), compare |
| Quantum (Deutsch) | 1 | Query in superposition, read phase via interference |
First we build the four oracle circuits. Each implements one of the four single-bit Boolean functions as a reversible quantum gate. The constant oracles ignore x; the balanced oracles use x as a control.
from qiskit_aer import AerSimulator
def create_constant_oracle(constant_value):
“””Creates a constant oracle (returns 0 or 1 for all inputs)”””
oracle = QuantumCircuit(2, name=f”Constant_{constant_value}”)
if constant_value == 1:
oracle.x(1) # Flip the output qubit
return oracle
def create_balanced_oracle(balance_type):
“””Creates a balanced oracle”””
oracle = QuantumCircuit(2, name=f”Balanced_{balance_type}”)
if balance_type == ‘identity’:
# f(x) = x
oracle.cx(0, 1)
elif balance_type == ‘negation’:
# f(x) = NOT x
oracle.x(0)
oracle.cx(0, 1)
oracle.x(0)
return oracle
The classical algorithm calls the oracle twice: once with input 0 to learn f(0), and once with input 1 to learn f(1). Each query is a separate circuit run.
“””First query: Evaluate f(0)”””
qr = QuantumRegister(2, ‘q’)
cr = ClassicalRegister(1, ‘c’)
qc = QuantumCircuit(qr, cr)
# Input: x = 0 (already initialised to |0>)
qc.barrier()
qc.compose(oracle, inplace=True)
qc.barrier()
qc.measure(1, 0) # Measure output to get f(0)
return qc
def classical_deutsch_query2(oracle):
“””Second query: Evaluate f(1)”””
qr = QuantumRegister(2, ‘q’)
cr = ClassicalRegister(1, ‘c’)
qc = QuantumCircuit(qr, cr)
qc.x(0) # Input: x = 1
qc.barrier()
qc.compose(oracle, inplace=True)
qc.barrier()
qc.measure(1, 0) # Measure output to get f(1)
return qc
The quantum circuit initialises q[1] = |1⟩, applies Hadamard gates to both wires, queries the oracle exactly once, then applies a final Hadamard to q[0] before measuring it. The oracle is never called again.
“””Implements Deutsch’s algorithm — requires only ONE query”””
qr = QuantumRegister(2, ‘q’)
cr = ClassicalRegister(1, ‘c’)
qc = QuantumCircuit(qr, cr)
# Step 1: Initialise q[1] to |1>
qc.x(1)
qc.barrier()
# Step 2: Hadamard on both wires (superposition)
qc.h(0)
qc.h(1)
qc.barrier()
# Step 3: Single oracle query
qc.compose(oracle, inplace=True)
qc.barrier()
# Step 4: Final Hadamard on q[0]
qc.h(0)
qc.barrier()
# Step 5: Measure q[0] — the INPUT qubit, not the output
qc.measure(0, 0)
return qc
The quantum circuit measures q[0], the input qubit. The classical circuits measure q[1], the output qubit. That structural difference is the entire quantum advantage: the quantum algorithm reads a global property of the function via interference, not an individual function value.
Test all four oracles with both approaches and compare query counts.
(“Constant 0”, create_constant_oracle(0)),
(“Constant 1”, create_constant_oracle(1)),
(“Balanced (Identity)”, create_balanced_oracle(‘identity’)),
(“Balanced (Negation)”, create_balanced_oracle(‘negation’))
]
simulator = AerSimulator()
for name, oracle in oracles:
# Classical: 2 queries
f_0 = int(list(simulator.run(classical_deutsch_query1(oracle), shots=1).result().get_counts().keys())[0])
f_1 = int(list(simulator.run(classical_deutsch_query2(oracle), shots=1).result().get_counts().keys())[0])
classical_result = “CONSTANT” if f_0 == f_1 else “BALANCED”
# Quantum: 1 query
counts = simulator.run(deutsch_algorithm(oracle), shots=1000).result().get_counts()
quantum_result = “CONSTANT” if ‘0’ in counts else “BALANCED”
print(f”{name}: Classical={classical_result}, Quantum={quantum_result}”)
Sample output:
Constant 1: Classical=CONSTANT, Quantum=CONSTANT
Balanced (Identity): Classical=BALANCED, Quantum=BALANCED
Balanced (Negation): Classical=BALANCED, Quantum=BALANCED
Classical Query 1 — Evaluate f(0)
q[0] stays in |0⟩, both wires pass through Uf, and q[1] is measured to read f(0).
Classical Query 2 — Evaluate f(1)
An X gate flips q[0] to |1⟩, both wires pass through Uf, and q[1] is measured to read f(1).
Quantum Deutsch — Single Query
H gates create superposition on both wires, one oracle query encodes f(0) ⊕ f(1) as a phase, the final H on q[0] converts phase to amplitude, and measuring q[0] gives the answer directly.
The measurement result on q[0] maps directly to the function type:
| Measure q[0] | Function type | Interference mechanism |
|---|---|---|
| 0 | Constant | f(0) ⊕ f(1) = 0 → constructive on |0⟩ |
| 1 | Balanced | f(0) ⊕ f(1) = 1 → destructive on |0⟩, constructive on |1⟩ |
The quantum speedup is 2x here (one query instead of two). Modest for a two-input function, but the same technique scales: Deutsch-Jozsa extends this to n-bit functions, cutting 2n−1+1 classical queries down to one. Simon’s algorithm extends it further. Shor’s algorithm for factoring, the most consequential quantum algorithm known, relies on the same architectural idea: query in superposition, encode information as phase, extract via interference and measurement.
pip install qiskit qiskit-aer. The full comparison script outputs all four oracle types side by side. Try modifying the oracle functions to explore how each gate sequence implements f.
What’s the difference between the classical and quantum implementation of Deutsch’s algorithm in Qiskit?
The classical version calls the oracle twice, once for each input, and measures the output qubit q[1] directly. The quantum version calls the oracle once, using superposition and phase kickback, then measures the input qubit q[0] after a final Hadamard to read a global property of the function.
Why does the quantum circuit measure q[0] instead of q[1]?
q[1] holds the output qubit and is never measured in the quantum version. The oracle imprints f(0) ⊕ f(1) onto q[0] as a phase via kickback; the final Hadamard on q[0] converts that phase into a measurable amplitude, so q[0] is where the answer ends up.
Is Deutsch’s algorithm useful in practice?
Not directly — a single-bit oracle problem has no real-world use case. Its value is pedagogical and architectural: the same query-in-superposition, encode-as-phase, extract-via-interference pattern scales up to Deutsch-Jozsa, Simon’s algorithm, and ultimately Shor’s factoring algorithm.
How many oracle queries does Deutsch’s algorithm save?
One query instead of two — a 2x reduction for this two-input case. The saving compounds for larger functions: Deutsch-Jozsa reduces 2n−1+1 classical queries to a single quantum query for an n-bit function.
Quantum Series 2026 · Built with Qiskit 1.x
✦ This article was generated with the assistance of Claude by Anthropic ✦
Share this:
- Share on Facebook (Opens in new window) Facebook
- Email a link to a friend (Opens in new window) Email
- Share on LinkedIn (Opens in new window) LinkedIn
- Share on Telegram (Opens in new window) Telegram
- Share on WhatsApp (Opens in new window) WhatsApp
- More
- Share on Bluesky (Opens in new window) Bluesky
- Print (Opens in new window) Print
- Share on Threads (Opens in new window) Threads
- Share on Reddit (Opens in new window) Reddit
- Share on Tumblr (Opens in new window) Tumblr
- Share on Pinterest (Opens in new window) Pinterest
- Share on X (Opens in new window) X
Leave a comment