QML
The qml package is the scikit-learn side of the library for quantum machine
learning (QML). It holds kernel support vector machines (SVMs) and quantum reservoir
estimators. Everything here follows the scikit-learn interface, with fit(X, y),
predict(X) and score(X, y), and runs without PyTorch.
Install: the qml extra (pip install eigenkit[qml]) installs scikit-learn, and
not PyTorch. The estimators run on the built-in NumPy simulator. To run them on
another backend, install its extra and pass provider=, for example
eigenkit[pennylane] and provider="pennylane".
QSVC
QSVC is the quantum support vector classifier. It passes the quantum kernel matrix
to scikit-learn's SVC(kernel="precomputed").
from eigenkit import QSVC
from eigenkit.datasets import load_dataset
data = load_dataset("iris")
X_train, y_train = data["X_train"], data["y_train"]
X_test, y_test = data["X_test"], data["y_test"]
model = QSVC(feature_map="AngleCNOT", reps=2, C=1.0)
model.fit(X_train, y_train)
predictions = model.predict(X_test)
The accepted feature_map values, what reps does, and how the kernel value is
computed are explained in Quantum Kernels. The defaults of QSVC,
feature_map="Angle" and reps=1, differ from those of QuantumKernel
("AngleCNOT" and reps=2).
Any other kernel, such as a custom map, a trained map, or a kernel on hardware, is
passed whole with kernel=. feature_map, reps and provider are then ignored,
and the fitted copy of the kernel is model.kernel_. scale multiplies the inputs
before they are encoded, so this bandwidth can be tuned like C:
from eigenkit import QuantumKernel
kernel = QuantumKernel(n_features=X_train.shape[1], feature_map="AngleCNOT", reps=2)
model = QSVC(kernel=kernel, scale=0.5).fit(X_train, y_train)
margins = model.decision_function(X_test)
decision_function(X) returns the margins of the SVM. probability=True enables
predict_proba through a sigmoid calibration, as in
CalibratedClassifierCV(SVC(), ensemble=False). The predictions still come from the
plain SVC.
QSVR
QSVR is the quantum support vector regressor. It works like QSVC, but it passes
the kernel matrix to scikit-learn's SVR(kernel="precomputed").
from eigenkit import QSVR
from eigenkit.datasets import load_dataset
data = load_dataset("concrete")
X_reg, y_reg = data["X_train"][:100], data["y_train"][:100]
model = QSVR(feature_map="Angle", reps=1, C=1.0, epsilon=0.1)
model.fit(X_reg, y_reg)
Use inside a scikit-learn Pipeline
The kernel estimators can also be the last step of a scikit-learn Pipeline. When
you tune them with GridSearchCV, prefix each parameter with the name of the step,
including the bandwidth scale:
from sklearn.datasets import make_moons
from sklearn.model_selection import GridSearchCV
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from eigenkit import QSVC
X, y = make_moons(n_samples=40, noise=0.1, random_state=0)
search = GridSearchCV(
Pipeline(
[
("scale", StandardScaler()),
(
"qsvc",
QSVC(
feature_map="Angle",
reps=1,
C=1.0,
probability=False,
),
),
]
),
param_grid={
"qsvc__C": [0.1, 1.0, 10.0],
"qsvc__feature_map": ["Angle", "AngleCNOT"],
"qsvc__scale": [0.5, 1.0],
},
scoring="accuracy",
cv=3,
)
search.fit(X, y)
predictions = search.predict(X[:5])
Quantum reservoir computing
QuantumReservoirClassifier and QuantumReservoirRegressor use a quantum circuit as
a fixed dynamical system that is not trained, called the reservoir. They train
only a cheap linear readout on the features that the reservoir produces. The
weights of the circuit are drawn once from random_state and then frozen, so there is
no gradient descent and no PyTorch. Fitting solves a ridge or logistic regression in
closed form.
The data is a batch of sequences. X has shape (n_sequences, n_timesteps,
n_features), and y has shape (n_sequences, n_timesteps), with one target per
time step.
import numpy as np
from eigenkit import Encoding, Variational
from eigenkit import QuantumReservoirRegressor
rng = np.random.default_rng(0)
X = rng.uniform(-1, 1, size=(40, 50, 3))
y = np.zeros((40, 50))
y[:, 1:] = X[:, :-1, 0] # predict the previous step's first channel
reservoir = QuantumReservoirRegressor(
blocks=[Encoding(in_features=3, rotation="RY"), Variational(depth=3, rotation="RY")],
state_policy="leaky",
leak=0.5, # fading memory
features="probabilities",
washout=5,
)
reservoir.fit(X, y)
print(reservoir.predict(X).shape) # (40, 50)
QuantumReservoirClassifier takes the same arguments, with one exception. It accepts
integer or label targets, reads out through a logistic model, and adds
predict_proba. The exception is the regularisation of the readout: the ridge of
the regressor (float, default 1e-06) becomes C on the classifier (float,
default 1.0). C is the inverse of the regularisation strength, so a larger C
means less regularisation.
| Parameter | Type | Default | Description |
|---|---|---|---|
blocks |
list[Block] |
Required | The frozen reservoir circuit. |
state_policy |
str |
"carry" |
One of "carry", "leaky" or "stateless". |
window |
int |
1 |
The number of time steps encoded per step with "stateless". The circuit must then encode window * n_features inputs. |
leak |
float |
1.0 |
The mixing rate for "leaky". At 1.0, it is the same as "carry". |
features |
str \| list[str] |
"z" |
One of "z", "probabilities" or "amplitudes", a Hamiltonian string such as "0.5 Z0 X1 - 0.3 Y0", or a list of observables. |
ridge |
float |
1e-06 |
The regularisation of the linear readout. |
washout |
int |
0 |
The number of first time steps dropped when the readout is fitted. Predictions are still produced for every time step. |
weight_scale |
float |
1.0 |
The scale of the frozen weights that are drawn. Larger values give more energetic dynamics. |
provider |
ProviderSpec |
"numpy" |
The backend, with the same options as Circuit(provider=...). |
random_state |
int \| None |
42 |
The seed of the frozen weights. The reservoir is random, but fixed. |
Fading memory matters
A purely unitary carry (state_policy="carry", the default) preserves the norm,
but it tends to scramble the history into a form that a linear readout cannot
recover. So it often has little memory that can be used. Some dissipation is what
makes a reservoir work in practice: "leaky" with a leak of about 0.3 to
0.7 is usually the right starting point for temporal tasks. The
memory capacity example shows this directly.
The layers under these wrappers, which are the driver, the policy classes, the feature extractors and the readout, are documented in Reservoir Computing.
API reference
eigenkit.qml.QSVC
Bases: BaseQuantumClassifier
Quantum support vector classifier (QSVC).
It computes a quantum kernel matrix from a fidelity circuit, and then uses
SVC(kernel="precomputed") from scikit-learn.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
feature_map
|
str
|
The feature map. It is |
'Angle'
|
reps
|
int
|
The number of repetitions of the feature map. Default: |
1
|
C
|
float
|
The regularisation parameter of the support vector machine. Default:
|
1.0
|
probability
|
bool
|
If True, the model gives Platt-scaled probabilities. If
False (the default), |
False
|
provider
|
ProviderSpec
|
The backend. It takes the same options as
|
'numpy'
|
random_state
|
int | None
|
The seed. |
42
|
kernel
|
QuantumKernel | None
|
A |
None
|
scale
|
float
|
The bandwidth. The inputs are multiplied by it before they reach
the kernel, so it can be tuned with |
1.0
|
Example
import numpy as np
from eigenkit import QSVC, Encoding, QuantumKernel, Variational
rng = np.random.default_rng(0)
X_train, X_test = rng.uniform(0, np.pi, (8, 4)), rng.uniform(0, np.pi, (3, 4))
y_train = np.array([0, 1] * 4)
model = QSVC(feature_map="AngleCNOT", reps=2)
model.fit(X_train, y_train)
predictions = model.predict(X_test)
# Any kernel works, including custom and trained ones.
my_map = [Encoding(in_features=4), Variational(depth=1)]
model = QSVC(kernel=QuantumKernel(n_features=4, feature_map=my_map))
__sklearn_is_fitted__()
Return True when the wrapped scikit-learn SVC has been fitted.
decision_function(X)
Return the signed distance of each sample to the separating hyperplane.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
ndarray
|
The samples, of shape |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
An array of shape |
fit(X, y)
Fit the classifier on training data.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
ndarray
|
The samples, of shape |
required |
y
|
ndarray
|
The class labels, of shape |
required |
Returns:
| Type | Description |
|---|---|
QSVC
|
This estimator, so that calls can be chained. |
predict(X)
Predict the class labels of X.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
ndarray
|
The samples, of shape |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
One prediction for each sample. |
predict_proba(X)
Predict the probability of each class. It requires probability=True.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
ndarray
|
The samples, of shape |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
The probabilities, of shape |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the model was created with |
eigenkit.qml.QSVR
Bases: BaseQuantumRegressor
Quantum support vector regressor (QSVR).
It is the regression counterpart of QSVC. It uses
SVR(kernel="precomputed") from scikit-learn on the quantum kernel matrix.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
feature_map
|
str
|
The feature map. It is |
'Angle'
|
reps
|
int
|
The number of repetitions of the feature map. Default: |
1
|
C
|
float
|
The regularisation parameter of the support vector regressor. Default:
|
1.0
|
epsilon
|
float
|
The width of the epsilon-insensitive tube. Default: |
0.1
|
provider
|
ProviderSpec
|
The backend. It takes the same options as
|
'numpy'
|
random_state
|
int | None
|
The seed. |
42
|
kernel
|
QuantumKernel | None
|
A |
None
|
scale
|
float
|
The bandwidth. The inputs are multiplied by it before they reach
the kernel. Default: |
1.0
|
Example
__sklearn_is_fitted__()
Return True when the wrapped scikit-learn SVR has been fitted.
fit(X, y)
Fit the regressor on training data.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
ndarray
|
The samples, of shape |
required |
y
|
ndarray
|
The float targets, of shape |
required |
Returns:
| Type | Description |
|---|---|
QSVR
|
This estimator, so that calls can be chained. |
predict(X)
Predict continuous targets for X.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
ndarray
|
The samples, of shape |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
One prediction for each sample. |
eigenkit.qml.QuantumReservoirClassifier
Bases: _ReservoirMixin, BaseQuantumClassifier
Quantum reservoir classifier: a fixed random reservoir and a logistic readout.
It uses the same reservoir as
QuantumReservoirRegressor.
The readout is a scikit-learn LogisticRegression on the features of each
timestep. y holds integer or label targets, of shape
(n_sequences, n_timesteps).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
blocks
|
list
|
The architecture of the reservoir circuit, a list of blocks that
is assembled once. Its |
required |
state_policy
|
str
|
|
'carry'
|
window
|
int
|
The length of the sliding window, for |
1
|
leak
|
float
|
The mixing coefficient |
1.0
|
features
|
FeaturesSpec
|
The features read from the reservoir. It is |
'z'
|
C
|
float
|
The inverse regularisation strength of the logistic readout, passed
to scikit-learn. Smaller values regularise more. This is the only
argument that the regressor does not have: there, the readout is a
ridge regression controlled by |
1.0
|
washout
|
int
|
The number of timesteps dropped from the fit of the readout. They are not dropped from the predictions. |
0
|
weight_scale
|
float
|
The standard deviation of the sampled reservoir weights. |
1.0
|
provider
|
ProviderSpec
|
The backend, which must give access to the statevector.
Default: |
'numpy'
|
random_state
|
int | None
|
The seed of the fixed reservoir weights. |
42
|
__sklearn_is_fitted__()
Return True when the readout has been fitted.
fit(X, y)
Fit the estimator on X and y.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
ndarray
|
The sequences, of shape
|
required |
y
|
ndarray
|
One target for each timestep, of shape
|
required |
Returns:
| Type | Description |
|---|---|
'QuantumReservoirClassifier'
|
This estimator, so that calls can be chained. |
predict(X)
Predict a label for every timestep of every sequence in X.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
ndarray
|
The sequences, of shape
|
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
The predicted labels, of shape |
predict_proba(X)
Predict the class probabilities for every timestep of every sequence in X.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
ndarray
|
The sequences, of shape
|
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
The probabilities, of shape |
score(X, y)
Score the estimator on X against y.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
ndarray
|
The sequences, of shape
|
required |
y
|
ndarray
|
One target for each timestep, of shape
|
required |
Returns:
| Type | Description |
|---|---|
float
|
The accuracy of the predictions, over every timestep of every sequence. |
eigenkit.qml.QuantumReservoirRegressor
Bases: _ReservoirMixin, BaseQuantumRegressor
Quantum reservoir regressor: a fixed random reservoir and a ridge readout.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
blocks
|
list
|
The architecture of the reservoir circuit, a list of blocks that
is assembled once. Its |
required |
state_policy
|
str
|
|
'carry'
|
window
|
int
|
The length of the sliding window, for |
1
|
leak
|
float
|
The mixing coefficient |
1.0
|
features
|
FeaturesSpec
|
The features read from the reservoir. It is |
'z'
|
ridge
|
float
|
The strength of the L2 penalty of the linear readout, which is solved in closed form. |
1e-06
|
washout
|
int
|
The number of timesteps dropped from the fit of the readout. They are not dropped from the predictions. |
0
|
weight_scale
|
float
|
The standard deviation of the sampled reservoir weights. |
1.0
|
provider
|
ProviderSpec
|
The backend, which must give access to the statevector.
Default: |
'numpy'
|
random_state
|
int | None
|
The seed of the fixed reservoir weights. |
42
|
__sklearn_is_fitted__()
Return True when the readout has been fitted.
fit(X, y)
Fit the estimator on X and y.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
ndarray
|
The sequences, of shape
|
required |
y
|
ndarray
|
One target for each timestep, of shape
|
required |
Returns:
| Type | Description |
|---|---|
'QuantumReservoirRegressor'
|
This estimator, so that calls can be chained. |
predict(X)
Predict a value for every timestep of every sequence in X.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
ndarray
|
The sequences, of shape
|
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
The predictions, of shape |
score(X, y)
Score the estimator on X against y.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
ndarray
|
The sequences, of shape
|
required |
y
|
ndarray
|
One target for each timestep, of shape
|
required |
Returns:
| Type | Description |
|---|---|
float
|
The coefficient of determination |