Source code for scikit_quri.qsvm.base_qsv

from __future__ import annotations

from enum import Enum

import numpy as np
from numpy.typing import NDArray
from quri_parts.circuit import QuantumCircuit
from quri_parts.core.sampling import ConcurrentSampler
from sklearn import svm

from scikit_quri.circuit import LearningCircuit
from scikit_quri.state import overlap_estimator


[docs]class SVMethodType(Enum): SVC = 1 SVR = 2
[docs]class BaseQSV: """Base class for Quantum Support Vector Machine.""" def __init__(self, circuit: LearningCircuit, sv_method_type: SVMethodType) -> None: self.circuit = circuit self.sv_method_type = sv_method_type self.data_circuits: list[QuantumCircuit] = [] self.n_qubit = circuit.n_qubits
[docs] def fit( self, x: NDArray[np.float64], y: NDArray[np.float64], sampler: ConcurrentSampler, n_shots: int = 1000, max_iter: int = int(1e7), verbose: bool = False, ) -> None: """Fit the model to the training data. Args: x: Training feature matrix of shape (n_samples, n_features). y: Training labels. sampler: Concurrent sampler function. n_shots: Number of shots per circuit execution. Defaults to 1000. max_iter: Maximum number of iterations for the SVM solver. Defaults to 1e7. verbose: Whether to print the SVM training progress. Defaults to False. """ n_x = len(x) gram_train = np.zeros((n_x, n_x)) self.data_circuits = [self._run_circuit(x[i]) for i in range(n_x)] self.estimator = overlap_estimator(sampler, n_shots) gram_train = self.estimator.estimate_concurrent( self.data_circuits, self.data_circuits ).reshape(n_x, n_x) # max_iter must be specified at instantiation time for sklearn SVM if self.sv_method_type == SVMethodType.SVC: self.sv_method = svm.SVC(kernel="precomputed", max_iter=max_iter, verbose=verbose) elif self.sv_method_type == SVMethodType.SVR: self.sv_method = svm.SVR(kernel="precomputed", max_iter=max_iter, verbose=verbose) self.sv_method.fit(gram_train, y) self.gram_train = gram_train
[docs] def predict(self, xs: NDArray[np.float64]) -> NDArray[np.float64]: """Predict outcomes for the given test data. Args: xs: Test feature matrix of shape (n_samples, n_features). Returns: pred: Predicted values of shape (n_samples,). """ if not self.estimator: raise ValueError("run fit() before predict") n_x = len(xs) gram_test = np.zeros((n_x, len(self.data_circuits))) test_circuits = [self._run_circuit(xs[i]) for i in range(n_x)] gram_test = self.estimator.estimate_concurrent(test_circuits, self.data_circuits).reshape( n_x, len(self.data_circuits) ) print() pred: NDArray[np.float64] = self.sv_method.predict(gram_test) return pred
def _run_circuit(self, x: NDArray[np.float64]) -> QuantumCircuit: """Return a bound circuit with the input data applied.""" return self.circuit.bind_input_and_parameters(x, np.array([])).get_mutable_copy()
[docs]class QSVC(BaseQSV): """Quantum Support Vector Classifier. Args: circuit: LearningCircuit """ def __init__(self, circuit: LearningCircuit) -> None: super().__init__(circuit, SVMethodType.SVC)
[docs]class QSVR(BaseQSV): """Quantum Support Vector Regressor. Args: circuit: LearningCircuit """ def __init__(self, circuit: LearningCircuit) -> None: super().__init__(circuit, SVMethodType.SVR)