Loading...
Loading...
Solving linear systems Ax = b exponentially faster
The Harrow-Hassidim-Lloyd (HHL) algorithm solves systems of linear equations Ax = b on a quantum computer with an exponential speedup over classical methods for certain classes of sparse, well-conditioned matrices. It combines Quantum Phase Estimation, controlled Hamiltonian simulation, and conditional rotation to encode the solution vector x into a quantum state. The algorithm is a cornerstone of quantum machine learning and scientific computing, though it produces a quantum state rather than the full classical vector, limiting its direct applicability.
Given an N × N Hermitian matrix A and a vector b, the linear system problem asks us to find x such that Ax = b. Classically, solving this system exactly using Gaussian elimination requires O(N³) operations, while iterative methods for sparse matrices require O(N s κ log(1/ε)) operations, where s is the sparsity, κ is the condition number, and ε is the desired precision. For very large systems arising in machine learning, partial differential equations, and optimization, these costs become prohibitive.
The HHL algorithm assumes that A is Hermitian (or can be made so by embedding), s-sparse (each row has at most s nonzero entries), and has condition number κ = |λ_max| / |λ_min|. The vector b is provided as a quantum state |b⟩, and the algorithm produces a quantum state |x⟩ proportional to A^{-1}|b⟩. The runtime scales as O(log(N) s² κ² / ε), which is exponentially faster in N than classical methods. However, reading out all components of x classically still requires O(N) measurements, so the speedup is most valuable when the goal is to compute global properties of x such as expectation values ⟨x|M|x⟩.
Linear system
Spectral decomposition
Condition number
The HHL algorithm consists of three main stages, each operating on distinct quantum registers. First, Quantum Phase Estimation is performed on the unitary U = e^{iAt} using the b-register as the eigenstate register and a clock register of n qubits. This encodes the eigenvalues λ_j of A into the computational basis states of the clock register, transforming the state to ∑_j β_j |λ̃_j⟩ |u_j⟩, where |b⟩ = ∑_j β_j |u_j⟩ and |λ̃_j⟩ is the binary approximation of λ_j.
Second, a conditional rotation is applied to an ancilla qubit controlled on the clock register. The rotation angle for each eigenvalue component is chosen to be arcsin(C/λ̃_j), where C is a normalization constant satisfying C ≤ min_j |λ_j|. This operation entangles the ancilla with the other registers, giving amplitudes proportional to C/λ_j for the ancilla being in |1⟩. Third, the inverse QPE is applied to uncompute the clock register, returning it to |0⟩^⊗n. Conditioning on measuring the ancilla in |1⟩ yields the state |x⟩ ∝ ∑_j (β_j/λ_j) |u_j⟩, which is exactly the solution to the linear system.
After QPE
Conditional rotation
Solution state
The HHL algorithm achieves a query complexity of O(κ² / ε) to the oracle that simulates e^{iAt}, with a total gate count of O(log(N) s² κ² / ε) for s-sparse matrices. This represents an exponential improvement over classical dense methods and a polynomial improvement over classical sparse solvers for certain parameter regimes. However, several important caveats limit its practical applicability on both current and future quantum hardware.
First, state preparation — encoding the classical vector b into a quantum state |b⟩ — generally requires O(N) operations unless b has special structure. Second, the output is a quantum state |x⟩, and extracting all N classical entries requires O(N) measurements, destroying the exponential speedup. The true value of HHL lies in computing expectation values ⟨x|M|x⟩ for some observable M, which can be done in O(log N) time if M is efficiently measurable. Third, the algorithm requires deep circuits with O(κ) sequential oracle calls, demanding long coherence times. Improved versions using variable-time amplitude amplification reduce the dependence on κ from κ² to κ, bringing the complexity closer to optimal.
Original runtime
Optimal runtime (improved)
Runnable implementations you can copy and experiment with.
A simplified HHL circuit for a 1-qubit system with A = diag(1, 1/2) and |b⟩ = (|0⟩ + |1⟩)/√2, demonstrating all three stages.
from qiskit import QuantumCircuit
from qiskit.circuit.library import QFT
import numpy as np
n_clock = 3
b = 3
anc = 4
qc = QuantumCircuit(n_clock + 2, 2)
# Prepare |b> = (|0> + |1>)/sqrt(2)
qc.h(b)
# Stage 1: Quantum Phase Estimation
for q in range(n_clock):
qc.h(q)
# Controlled-U^{2^k} for A = diag(1, 1/2), t = pi/2
for k, q in enumerate(range(n_clock)):
pow2 = 2 ** k
qc.p(np.pi / 2 * pow2, q)
qc.cp(-np.pi / 4 * pow2, q, b)
# Inverse QFT
qc = qc.compose(QFT(n_clock, inverse=True), range(n_clock))
# Stage 2: Controlled rotation on ancilla (C = 0.5)
# |001> corresponds to lambda = 1/2
qc.x(1)
qc.x(2)
qc.mcry(np.pi / 2, [0, 1, 2], anc)
qc.x(2)
qc.x(1)
# |010> corresponds to lambda = 1
qc.x(0)
qc.x(2)
qc.mcry(np.pi / 6, [0, 1, 2], anc)
qc.x(2)
qc.x(0)
# Stage 3: Uncompute QPE
qc = qc.compose(QFT(n_clock, inverse=False), range(n_clock))
for q in reversed(range(n_clock)):
pow2 = 2 ** q
qc.cp(np.pi / 4 * pow2, q, b)
qc.p(-np.pi / 2 * pow2, q)
qc.h(q)
# Measure ancilla and b-register
qc.measure(anc, 0)
qc.measure(b, 1)
print(qc.draw(output='text'))A PennyLane implementation of the HHL algorithm for the same 1-qubit linear system, returning joint probabilities of the b-register and ancilla.
import pennylane as qml
import numpy as np
n_clock = 3
b = 3
anc = 4
dev = qml.device('default.qubit', wires=n_clock + 2)
@qml.qnode(dev)
def hhl():
# Prepare |b> = (|0> + |1>)/sqrt(2)
qml.Hadamard(wires=b)
# Stage 1: Quantum Phase Estimation
for q in range(n_clock):
qml.Hadamard(wires=q)
for k, q in enumerate(range(n_clock)):
pow2 = 2 ** k
qml.PhaseShift(np.pi / 2 * pow2, wires=q)
qml.ControlledPhaseShift(-np.pi / 4 * pow2, wires=[q, b])
qml.adjoint(qml.QFT)(wires=range(n_clock))
# Stage 2: Controlled rotation on ancilla (C = 0.5)
# |001> corresponds to lambda = 1/2
qml.PauliX(wires=1)
qml.PauliX(wires=2)
qml.ctrl(qml.RY, control=[0, 1, 2])(np.pi / 2, wires=anc)
qml.PauliX(wires=2)
qml.PauliX(wires=1)
# |010> corresponds to lambda = 1
qml.PauliX(wires=0)
qml.PauliX(wires=2)
qml.ctrl(qml.RY, control=[0, 1, 2])(np.pi / 6, wires=anc)
qml.PauliX(wires=2)
qml.PauliX(wires=0)
# Stage 3: Uncompute QPE
qml.QFT(wires=range(n_clock))
for q in reversed(range(n_clock)):
pow2 = 2 ** q
qml.ControlledPhaseShift(np.pi / 4 * pow2, wires=[q, b])
qml.PhaseShift(-np.pi / 2 * pow2, wires=q)
qml.Hadamard(wires=q)
return qml.probs(wires=[b, anc])
print(hhl())