Source code for qlearnkit.algorithms.qknn.qknn_classifier
from sklearn.exceptions import NotFittedError
import logging
import numpy as np
from qiskit.providers import BaseBackend, Backend
from qiskit.utils import QuantumInstance
from typing import Optional, Union
from sklearn.base import ClassifierMixin
import scipy.stats as stats
from .qknn_base import QNeighborsBase
from ...encodings import EncodingMap
logger = logging.getLogger(__name__)
[docs]class QKNeighborsClassifier(ClassifierMixin, QNeighborsBase):
r"""
The Quantum K-Nearest Neighbors algorithm for classification
Note:
The naming conventions follow the KNeighborsClassifier from
sklearn.neighbors
Example:
Classify data using the Iris dataset.
.. jupyter-execute::
import numpy as np
from qlearnkit.algorithms import QKNeighborsClassifier
from qlearnkit.encodings import AmplitudeEncoding
from qiskit import BasicAer
from qiskit.utils import QuantumInstance, algorithm_globals
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
seed = 42
algorithm_globals.random_seed = seed
quantum_instance = QuantumInstance(BasicAer.get_backend('qasm_simulator'),
shots=1024,
optimization_level=1,
seed_simulator=seed,
seed_transpiler=seed)
encoding_map = AmplitudeEncoding()
# Use iris data set for training and test data
X, y = load_iris(return_X_y=True)
num_features = 2
X = np.asarray([x[0:num_features] for x, y_ in zip(X, y) if y_ != 2])
y = np.asarray([y_ for x, y_ in zip(X, y) if y_ != 2])
qknn = QKNeighborsClassifier(
n_neighbors=3,
quantum_instance=quantum_instance,
encoding_map=encoding_map
)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.20, random_state=seed)
qknn.fit(X_train, y_train)
print(f"Testing accuracy: "
f"{qknn.score(X_test, y_test):0.2f}")
"""
def __init__(self,
n_neighbors: int = 3,
encoding_map: Optional[EncodingMap] = None,
quantum_instance: Optional[Union[QuantumInstance, BaseBackend, Backend]] = None):
"""
Creates a QKNeighborsClassifier Object
Args:
n_neighbors:
number of neighbors participating in the
majority vote
encoding_map:
map to classical data to quantum states.
This class does not impose any constraint on it.
quantum_instance:
the quantum instance to set. Can be a
:class:`~qiskit.utils.QuantumInstance`, a :class:`~qiskit.providers.Backend`
or a :class:`~qiskit.providers.BaseBackend`
"""
super().__init__(n_neighbors, encoding_map, quantum_instance)
def _majority_voting(self,
y_train: np.ndarray,
fidelities: np.ndarray) -> np.ndarray:
r"""
Performs the classical majority vote procedure of the
K-Nearest Neighbors classifier with the :math:`k` nearest to
determine class
Args:
y_train: the train labels
fidelities: the list ``F`` of fidelities used as a measure of
distance
Returns:
a list of predicted labels for the test data
Raises:
ValueError if :math:`\exists f \in F \ t.c. f \notin [0, 1]`, assuming
a tolerance of `0.2`
"""
k_nearest = self._kneighbors(y_train, fidelities)
# getting most frequent values in `k_nearest`
# in a more efficient way than looping and
# using, for instance, collections.Counter
n_queries, _ = self.X_train.shape
if n_queries == 1:
# case of 1D array
labels, _ = stats.mode(k_nearest)
else:
labels, _ = stats.mode(k_nearest, axis=1)
# eventually flatten the np.ndarray
# returned by stats.mode
return labels.real.flatten()
[docs] def predict(self,
X_test: np.ndarray) -> np.ndarray:
"""Predict the labels of the provided data."""
if self.X_train is None:
raise NotFittedError(
"This QKNeighborsClassifier instance is not fitted yet. "
"Call 'fit' with appropriate arguments before using "
"this estimator.")
circuits = self._construct_circuits(X_test)
results = self.execute(circuits)
# the execution results are employed to compute
# fidelities which are used for the majority voting
fidelities = self._get_fidelities(results, len(X_test))
return self._majority_voting(self.y_train, fidelities)