Deutsch Algorithm Revisited: Quantum vs Classical Implementation in Qiskit

QUANTUM SERIES 2026
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.


1  ·  The Challenge

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
The quantum advantage: instead of probing the function at individual inputs, the algorithm queries it at a superposition of both inputs simultaneously, then uses interference to extract a global property (constant vs balanced) with a single measurement.
2  ·  Oracle Functions

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 import QuantumCircuit, QuantumRegister, ClassicalRegister
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
3  ·  Classical Approach: Two Queries Required

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.

def classical_deutsch_query1(oracle):
    “””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
Classical bottleneck: two separate measurements, two separate circuit executions. The classical algorithm measures the output qubit q[1] in both cases to read the function values directly.
4  ·  Quantum Approach: One Query Suffices

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.

def deutsch_algorithm(oracle):
    “””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.

5  ·  Running the Comparison

Test all four oracles with both approaches and compare query counts.

oracles = [
    (“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 0:          Classical=CONSTANT, Quantum=CONSTANT
Constant 1:          Classical=CONSTANT, Quantum=CONSTANT
Balanced (Identity): Classical=BALANCED, Quantum=BALANCED
Balanced (Negation): Classical=BALANCED, Quantum=BALANCED
Both methods always agree. The quantum result is deterministic: the simulator returns 100% |0⟩ for constant oracles and 100% |1⟩ for balanced ones, with zero noise on shots=1000.
6  ·  Circuit Overview

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).

q[0]: |0⟩ (not measured)
Uf
q[1]: |0⟩ M 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).

q[0]: |0⟩ X (not measured)
Uf
q[1]: |0⟩ M 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.

q[0]: |0⟩ H H M 0=const, 1=bal
Uf
q[1]: |1⟩ H
The structural difference: the classical circuits measure the output qubit q[1] to read individual function values. The quantum circuit measures the input qubit q[0] after interference to read a global property of the function. That is what lets a single query reveal whether the function is constant or balanced.
7  ·  Measurement Interpretation and Conclusion

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.

To run the code: 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.

Quantum Series 2026  ·  Built with Qiskit 1.x

✦ This article was generated with the assistance of Claude by Anthropic