Source code for psipose.models.kernel

"""Quantum kernel - pure kernel model.

This module implements quantum kernel methods based on state fidelity,
which is a core technique in quantum support vector machines.
"""

import numpy as np
import pennylane as qml
from sklearn.base import BaseEstimator
from sklearn.utils.validation import check_is_fitted

from psipose.feature_maps import AngleFeatureMap


[docs] class QuantumKernel(BaseEstimator): """Quantum kernel using fidelity between encoded states. Computes the Gram matrix where each entry is the fidelity (squared inner product) between quantum-encoded data points: K[i, j] = |⟨φ(x_i) | φ(x_j)⟩|^2 This quantum kernel approach was introduced in the landmark paper: - Havlíček et al. (2019), "Supervised learning with quantum-inspired kernel" (arXiv:1904.01567). The paper demonstrates how quantum circuits can implicitly define kernel functions that may provide quantum advantage for classification tasks. The kernel computation uses the overlap circuit method: by applying the adjoint of the encoding circuit for x_j after the encoding for x_i, the probability of measuring the all-zeros state equals the squared fidelity between the two encoded quantum states. Parameters ---------- feature_map : FeatureMap, optional The quantum feature map. If None, uses AngleFeatureMap() with n_qubits. n_qubits : int, default=4 Number of qubits. Only used if feature_map is None. device : str, default="default.qubit" PennyLane device. batch_size : int, optional Number of circuits to evaluate in parallel. Attributes ---------- feature_map_ : FeatureMap Fitted feature map. n_qubits_ : int Number of qubits used. References ---------- .. [1] V. Havlíček et al., "Supervised learning with quantum-inspired kernel," arXiv:1904.01567, 2019. """ def __init__( self, feature_map=None, n_qubits=4, device="default.qubit", batch_size=None ): self.feature_map = feature_map self.n_qubits = n_qubits self.device = device self.batch_size = batch_size
[docs] def fit(self, X): """Fit the kernel. Parameters ---------- X : array-like, shape (n_samples, n_features) Training data. Returns ------- self : QuantumKernel """ if self.feature_map is None: self.feature_map_ = AngleFeatureMap(n_qubits=self.n_qubits) else: self.feature_map_ = self.feature_map self.feature_map_.fit(X) self.n_qubits_ = self.feature_map_.n_qubits_ self.encoder_ = self.feature_map_ return self
def _compute_overlap(self, x1, x2): """Compute fidelity between two encoded states.""" dev = qml.device(self.device, wires=self.n_qubits_) @qml.qnode(dev, interface="autograd") def circuit(): self.feature_map_.encode(x1, wires=range(self.n_qubits_)) qml.adjoint(self.feature_map_.encode)(x2, wires=range(self.n_qubits_)) return qml.probs(wires=range(self.n_qubits_)) probs = circuit() return probs[0]
[docs] def compute_matrix(self, X, Y=None): """Compute the kernel matrix. Parameters ---------- X : array-like, shape (n_samples_X, n_features) First set. Y : array-like, shape (n_samples_Y, n_features), optional Second set. If None, uses X. Returns ------- K : ndarray, shape (n_samples_X, n_samples_Y) Kernel matrix. """ check_is_fitted(self, ["feature_map_", "n_qubits_"]) if Y is None: Y = X symmetric = True else: symmetric = False n_samples_X = len(X) n_samples_Y = len(Y) K = np.zeros((n_samples_X, n_samples_Y)) for i in range(n_samples_X): for j in range(n_samples_Y): if symmetric and j < i: K[i, j] = K[j, i] else: K[i, j] = self._compute_overlap(X[i], Y[j]) return K
def __call__(self, X, Y=None): """Make kernel callable like sklearn kernel.""" return self.compute_matrix(X, Y)