Skip to content

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" (angle encoding into a product state) or "AngleCNOT" (angle encoding with ladders of controlled-NOT (CNOT) gates). Default: "Angle". It is ignored when kernel is given.

'Angle'
reps int

The number of repetitions of the feature map. Default: 1. It is ignored when kernel is given.

1
C float

The regularisation parameter of the support vector machine. Default: 1.0.

1.0
probability bool

If True, the model gives Platt-scaled probabilities. If False (the default), predict_proba is unavailable. The calibration is a sigmoid fitted by cross-validation on the training kernel, as in CalibratedClassifierCV(SVC(), ensemble=False). The predictions still come from the plain SVC.

False
provider ProviderSpec

The backend. It takes the same options as Circuit(provider=...). Any backend with a statevector or a zero_state_probability method works, including Qiskit Runtime and IQM hardware. Default: "numpy". It is ignored when kernel is given.

'numpy'
random_state int | None

The seed.

42
kernel QuantumKernel | None

A QuantumKernel to use instead of building one from feature_map, reps and provider. It can be a custom kernel, a trained kernel or a hardware kernel. Default: None.

None
scale float

The bandwidth. The inputs are multiplied by it before they reach the kernel, so it can be tuned with GridSearchCV. Default: 1.0.

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 (n_samples, n_features).

required

Returns:

Type Description
ndarray

An array of shape (n_samples,) for two classes, and (n_samples, n_classes) otherwise.

fit(X, y)

Fit the classifier on training data.

Parameters:

Name Type Description Default
X ndarray

The samples, of shape (n_samples, n_features).

required
y ndarray

The class labels, of shape (n_samples,).

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 (n_samples, n_features).

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 (n_samples, n_features).

required

Returns:

Type Description
ndarray

The probabilities, of shape (n_samples, n_classes), with the columns in the order of classes_.

Raises:

Type Description
ValueError

If the model was created with probability=False.

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" (angle encoding into a product state) or "AngleCNOT" (angle encoding with ladders of controlled-NOT (CNOT) gates). Default: "Angle". It is ignored when kernel is given.

'Angle'
reps int

The number of repetitions of the feature map. Default: 1. It is ignored when kernel is given.

1
C float

The regularisation parameter of the support vector regressor. Default: 1.0.

1.0
epsilon float

The width of the epsilon-insensitive tube. Default: 0.1.

0.1
provider ProviderSpec

The backend. It takes the same options as Circuit(provider=...). Default: "numpy". It is ignored when kernel is given.

'numpy'
random_state int | None

The seed.

42
kernel QuantumKernel | None

A QuantumKernel to use instead of building one from feature_map, reps and provider. Default: None.

None
scale float

The bandwidth. The inputs are multiplied by it before they reach the kernel. Default: 1.0.

1.0
Example
import numpy as np
from eigenkit import QSVR

rng = np.random.default_rng(0)
X_train, X_test = rng.uniform(0, np.pi, (8, 2)), rng.uniform(0, np.pi, (3, 2))
y_train = np.sin(X_train).sum(axis=1)

model = QSVR(feature_map="Angle", reps=1)
model.fit(X_train, y_train)
predictions = model.predict(X_test)

__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 (n_samples, n_features).

required
y ndarray

The float targets, of shape (n_samples,).

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 (n_samples, n_features).

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 n_inputs must match the policy: F for the policies that carry the state, and window * F for state_policy="stateless", where F is the number of features per timestep.

required
state_policy str

"carry" (the unitary of the circuit carries the state from one step to the next), "stateless" (each step starts from |0…0⟩ and encodes a sliding window of inputs), or "leaky" (the carried state is mixed with a reset state and renormalised).

'carry'
window int

The length of the sliding window, for state_policy="stateless".

1
leak float

The mixing coefficient alpha, for state_policy="leaky".

1.0
features FeaturesSpec

The features read from the reservoir. It is "z" (the value of ⟨Z⟩ on each qubit, the default), "probabilities", "amplitudes", a Pauli or Hamiltonian string (for example "0.5 Z0 X1 - 0.3 Y0"), or a list of such strings, to read out any observables.

'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 ridge.

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".

'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 (n_sequences, n_timesteps, n_features). A 2-D array is rejected.

required
y ndarray

One target for each timestep, of shape (n_sequences, n_timesteps).

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 (n_sequences, n_timesteps, n_features). A 2-D array is rejected.

required

Returns:

Type Description
ndarray

The predicted labels, of shape (n_sequences, n_timesteps).

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 (n_sequences, n_timesteps, n_features). A 2-D array is rejected.

required

Returns:

Type Description
ndarray

The probabilities, of shape (n_sequences, n_timesteps, n_classes), with the last axis in the order of classes_.

score(X, y)

Score the estimator on X against y.

Parameters:

Name Type Description Default
X ndarray

The sequences, of shape (n_sequences, n_timesteps, n_features). A 2-D array is rejected.

required
y ndarray

One target for each timestep, of shape (n_sequences, n_timesteps).

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 n_inputs must match the policy: F for the policies that carry the state, and window * F for state_policy="stateless", where F is the number of features per timestep.

required
state_policy str

"carry" (the unitary of the circuit carries the state from one step to the next), "stateless" (each step starts from |0…0⟩ and encodes a sliding window of inputs), or "leaky" (the carried state is mixed with a reset state and renormalised).

'carry'
window int

The length of the sliding window, for state_policy="stateless".

1
leak float

The mixing coefficient alpha, for state_policy="leaky".

1.0
features FeaturesSpec

The features read from the reservoir. It is "z" (the value of ⟨Z⟩ on each qubit, the default), "probabilities", "amplitudes", a Pauli or Hamiltonian string (for example "0.5 Z0 X1 - 0.3 Y0"), or a list of such strings, to read out any observables.

'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".

'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 (n_sequences, n_timesteps, n_features). A 2-D array is rejected.

required
y ndarray

One target for each timestep, of shape (n_sequences, n_timesteps), or (n_sequences, n_timesteps, n_outputs) for several outputs.

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 (n_sequences, n_timesteps, n_features). A 2-D array is rejected.

required

Returns:

Type Description
ndarray

The predictions, of shape (n_sequences, n_timesteps), or (n_sequences, n_timesteps, n_outputs) when the targets had several outputs.

score(X, y)

Score the estimator on X against y.

Parameters:

Name Type Description Default
X ndarray

The sequences, of shape (n_sequences, n_timesteps, n_features). A 2-D array is rejected.

required
y ndarray

One target for each timestep, of shape (n_sequences, n_timesteps).

required

Returns:

Type Description
float

The coefficient of determination R² of the predictions, over every timestep of every sequence.