Loading...
Loading...
Computing kernel matrices via quantum feature maps
Quantum Kernel Estimation computes the similarity between data points by encoding them into quantum states and estimating their overlap. For carefully designed feature maps, this overlap can encode correlations that are exponentially hard to compute classically, offering a pathway to quantum advantage in machine learning.
A quantum feature map is a parameterized quantum circuit U_φ(x) that encodes a classical data point x into an n-qubit quantum state |φ(x)⟩. The design of this circuit is crucial: it must be expressive enough to capture relevant structure in the data, yet shallow enough to run on near-term hardware with acceptable fidelity.
Common designs include the ZZFeatureMap, which encodes pairwise interactions between features via entangling ZZ rotations, and the PauliFeatureMap, which uses tensor products of Pauli operators to create increasingly complex manifolds in Hilbert space. The choice of entanglement strategy—linear, circular, or full—directly affects the class of correlations the kernel can detect.
From a geometric perspective, the quantum feature map induces a Hilbert-space metric on the input data. Two points that are close in this metric will have large kernel values, while distant points will have small ones. Because the Hilbert space dimension grows exponentially with the number of qubits, the induced geometry can be far richer than any classical kernel operating on the original feature space.
Feature map unitary
ZZFeatureMap
Kernel from overlap
Once a feature map is chosen, estimating the kernel entry between two points requires a quantum circuit that computes the overlap of their encoded states. The most direct method is the swap-test circuit: prepare both states independently, apply a controlled-swap conditioned on an ancilla qubit, and measure the ancilla. The probability of measuring the ancilla in the |0⟩ state is (1 + |⟨φ(x_i)|φ(x_j)⟩|²)/2.
An alternative that uses fewer qubits is the fidelity circuit: apply U_φ(x_i) to |0⟩⊗n, followed by the adjoint of U_φ(x_j). The squared amplitude of the all-zero state is exactly the kernel value. This approach is particularly efficient when the feature map is composed of unitary gates with known inverses, as is the case for standard angle-embedding circuits.
In practice, kernel estimation is performed with a finite number of shots, introducing shot noise into the kernel matrix. This noise can be mitigated by increasing the shot budget or by using advanced estimation techniques such as the median-of-means estimator. The resulting noisy kernel matrix is then fed into classical kernel methods such as SVMs, Gaussian processes, or kernel ridge regression.
Swap-test probability
Fidelity estimation
Shot-noise variance
Runnable implementations you can copy and experiment with.
This Qiskit example demonstrates how to construct a FidelityQuantumKernel using a ZZFeatureMap and the ComputeUncompute fidelity routine. The kernel matrix for a small dataset is printed.
from qiskit.circuit.library import ZZFeatureMap
from qiskit_machine_learning.kernels import FidelityQuantumKernel
from qiskit_machine_learning.state_fidelities import ComputeUncompute
from qiskit.primitives import Sampler
import numpy as np
feature_map = ZZFeatureMap(feature_dimension=2, reps=2, entanglement="linear")
sampler = Sampler()
fidelity = ComputeUncompute(sampler=sampler)
kernel = FidelityQuantumKernel(feature_map=feature_map, fidelity=fidelity)
X = np.array([[0, 0], [1, 0], [0.5, 0.5], [1, 1]])
K = kernel.evaluate(x_vec=X)
print("Quantum kernel matrix:")
print(np.round(K, 3))This PennyLane example defines a custom feature map and computes the quantum kernel as the squared overlap of the resulting quantum states. The kernel matrix is assembled for a small dataset.
import pennylane as qml
import pennylane.numpy as np
n_qubits = 2
dev = qml.device("default.qubit", wires=n_qubits)
@qml.qnode(dev)
def feature_map(x):
for i in range(n_qubits):
qml.Hadamard(wires=i)
qml.RZ(x[i], wires=i)
for i in range(n_qubits - 1):
qml.CNOT(wires=[i, i + 1])
qml.RZ((np.pi - x[i]) * (np.pi - x[i + 1]), wires=i + 1)
qml.CNOT(wires=[i, i + 1])
return qml.state()
def quantum_kernel(x1, x2):
psi1 = feature_map(x1)
psi2 = feature_map(x2)
return np.abs(np.vdot(psi1, psi2)) ** 2
X = np.array([[0.1, 0.2], [0.4, 0.5]], requires_grad=False)
K = np.array([[quantum_kernel(xi, xj) for xj in X] for xi in X])
print("Kernel matrix:")
print(K)