## `eigenkit.qml`

Quantum machine learning: kernels, support vector machines, the estimator base classes compatible with scikit-learn, and saving and loading models.

This package needs scikit-learn, NumPy and the core of the library, but not PyTorch: the quantum kernels run on the NumPy interface. The hybrid estimators based on PyTorch (`HQNC`, `HQNR` and others) are in `eigenkit.torch.estimators`, and they build on the base classes exported here.

### `BaseQuantumClassifier`

Bases: `ClassifierMixin`, `BaseQuantumEstimator`

Base class for quantum classifiers. Its `score()` returns the accuracy.

#### `predict_proba(X)`

Predict the class probabilities.

Subclasses should return the probabilities in the order of `classes_`, with shape `(n_samples, n_classes)`. The default implementation raises a `NotImplementedError`.

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\_. |

### `BaseQuantumEstimator`

Bases: `BaseEstimator`, `ABC`

Base class, compatible with scikit-learn, for all quantum algorithms.

Parameters:

| Name           | Type           | Description                                                                                                                                                                                                   | Default                                                                                                                                                                                                                                                                                                                                                                  |
| -------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `provider`     | `ProviderSpec` | The backend. It can be a registered name ("pennylane", "qiskit", "braket", or any name passed to register()), a configuration instance (for example PennyLaneConfig(device_name=...)), or a backend instance. | `'numpy'`                                                                                                                                                                                                                                                                                                                                                                |
| `random_state` | \`int          | None\`                                                                                                                                                                                                        | The seed for reproducibility, applied inside fit() and load(). What it seeds depends on the estimator. The PyTorch estimators isolate the random number generator of PyTorch, so seeding one of them does not reseed the random stream of the caller. The kernel estimators are deterministic, and they take it only so that all the estimators have the same interface. |

#### `fit(X, y)`

Fit the estimator on `X` and `y`.

Parameters:

| Name | Type      | Description                                        | Default    |
| ---- | --------- | -------------------------------------------------- | ---------- |
| `X`  | `ndarray` | The samples, of shape (n_samples, n_features).     | *required* |
| `y`  | `ndarray` | One target for each sample, of shape (n_samples,). | *required* |

Returns:

| Type                     | Description                                   |
| ------------------------ | --------------------------------------------- |
| `'BaseQuantumEstimator'` | This estimator, so that calls can be chained. |

#### `load(path, *, provider=None)`

Load a fitted model from a `.eigenkit` bundle.

Load only bundles that you trust. A bundle can contain joblib pickles, and unpickling runs code, as with `torch.load` or `joblib.load`.

Parameters:

| Name       | Type           | Description | Default                                 |
| ---------- | -------------- | ----------- | --------------------------------------- |
| `path`     | \`str          | Path\`      | The path of the bundle.                 |
| `provider` | \`ProviderSpec | None\`      | A provider that replaces the saved one. |

Returns:

| Type                     | Description           |
| ------------------------ | --------------------- |
| `'BaseQuantumEstimator'` | The loaded estimator. |

Raises:

| Type        | Description                                                                                                                  |
| ----------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `TypeError` | If the method is called on a subclass and the bundle holds a different class. BaseQuantumEstimator.load() accepts any class. |

#### `predict(X)`

Predict a value for every sample in `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. |

#### `save(path)`

Save a fitted model to a `.eigenkit` bundle.

The bundles of models that hold scikit-learn objects (`QSVC`, `QSVR`, the reservoir estimators and the time-series estimators) contain joblib pickles, so loading one can run code. Share them only with people who trust you.

Parameters:

| Name   | Type  | Description | Default                                                                   |
| ------ | ----- | ----------- | ------------------------------------------------------------------------- |
| `path` | \`str | Path\`      | The path of the bundle. The suffix .eigenkit is added when it is missing. |

### `BaseQuantumRegressor`

Bases: `RegressorMixin`, `BaseQuantumEstimator`

Base class for quantum regressors. Its `score()` returns `R²`.

### `KernelAlignmentResult`

Outcome of align_kernel().

Attributes:

| Name            | Type                | Description                                                                                                                                                                                                                           |
| --------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `weights`       | `ndarray`           | The trained weights.                                                                                                                                                                                                                  |
| `kernel`        | `QuantumKernel`     | A copy of the input kernel with weights, ready for QSVC(kernel=...).                                                                                                                                                                  |
| `loss`          | `float`             | The loss at weights.                                                                                                                                                                                                                  |
| `initial_loss`  | `float`             | The loss at the starting weights.                                                                                                                                                                                                     |
| `loss_history`  | `tuple[float, ...]` | The loss after every iteration of Simultaneous Perturbation Stochastic Approximation (SPSA), or at every evaluation for a SciPy optimiser. With SPSA, weights is the point with the lowest loss seen, including the starting weights. |
| `n_evaluations` | `int`               | The number of kernel matrices computed.                                                                                                                                                                                               |
| `success`       | `bool`              | Whether the loss is lower than at the starting point.                                                                                                                                                                                 |

### `KernelJob`

A kernel matrix submitted to a backend, to be collected later.

QuantumKernel.submit_kernel_matrix() returns it. The handle records which pair each submitted circuit measured, so `result()` returns the assembled matrix instead of a flat list of probabilities. `save()` writes this information together with the job id, so that the matrix can be collected from a new session with QuantumKernel.retrieve_kernel_matrix().

#### `n_circuits`

The number of circuits that the job runs, one for each measured entry.

#### `job_id()`

Return the identifier of the job on the backend.

#### `result()`

Wait until the job finishes, and return the kernel matrix.

Returns:

| Type      | Description                                                                        |
| --------- | ---------------------------------------------------------------------------------- |
| `ndarray` | The kernel matrix, of shape (n_x, n_x) for a Gram matrix and (n_x, n_y) otherwise. |

#### `save(path)`

Write the job id and the layout of the pairs to a JSON file.

The setting of `psd_projection` is written too.

Parameters:

| Name   | Type  | Description | Default                                                                                       |
| ------ | ----- | ----------- | --------------------------------------------------------------------------------------------- |
| `path` | \`str | Path\`      | Where to write the file. Its suffix is replaced by .json, or .json is added when it has none. |

Returns:

| Type   | Description                   |
| ------ | ----------------------------- |
| `Path` | The path of the written file. |

#### `status()`

Return the current status of the job, as a string from the backend.

### `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.                                                                                                                                                            |
| `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. |
| `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

```python
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. |

### `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.                                                                                          |
| `kernel`       | \`QuantumKernel | None\`                                                                                                                                                                                                  | A QuantumKernel to use instead of building one from feature_map, reps and provider. Default: None. |
| `scale`        | `float`         | The bandwidth. The inputs are multiplied by it before they reach the kernel. Default: 1.0.                                                                                                              | `1.0`                                                                                              |

Example

```python
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. |

### `QuantumKernel`

Quantum kernel matrix `K[i, j] = fidelity(U(x_i), U(x_j))`.

Parameters:

| Name             | Type              | Description                                                                                                                                                                                                                                                                                                                                                              | Default                                                                                                                            |
| ---------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- |
| `n_features`     | `int`             | The number of features. For the built-in maps, it is also the number of qubits.                                                                                                                                                                                                                                                                                          | *required*                                                                                                                         |
| `feature_map`    | `FeatureMapLike`  | The feature map. It is "Angle", "AngleCNOT", or a custom forward map U(x; w). A custom map is a CircuitSpec, a CircuitBuilder, a block, a list of blocks, or a callable emit(builder, x, w) that writes the map onto a CircuitBuilder, given the input references x and the weight references w. A custom map must read exactly n_features inputs. Default: "AngleCNOT". | `'AngleCNOT'`                                                                                                                      |
| `reps`           | `int`             | The number of repetitions of a built-in feature map. reps=1 uses only the RX encoding, and reps>=2 adds repetitions of the RZ encoding (with ladders of controlled-NOT (CNOT) gates for "AngleCNOT"). It is ignored for a custom map. Default: 2.                                                                                                                        | `2`                                                                                                                                |
| `provider`       | `ProviderSpec`    | The backend. It takes the same options as Circuit(provider=...). Default: "numpy", which needs no extra.                                                                                                                                                                                                                                                                 | `'numpy'`                                                                                                                          |
| `weights`        | \`Sequence[float] | ndarray                                                                                                                                                                                                                                                                                                                                                                  | None\`                                                                                                                             |
| `scale`          | `float`           | The bandwidth. Every input is multiplied by it before it reaches the circuit, so scale sets how far apart two samples are in Hilbert space. Default: 1.0.                                                                                                                                                                                                                | `1.0`                                                                                                                              |
| `n_weights`      | \`int             | None\`                                                                                                                                                                                                                                                                                                                                                                   | The number of weights that a callable feature_map reserves. For any other map, it is read from the circuit and, if given, checked. |
| `n_qubits`       | \`int             | None\`                                                                                                                                                                                                                                                                                                                                                                   | The number of qubits of a callable feature_map. Default: n_features.                                                               |
| `psd_projection` | `bool`            | If True, a sampled Gram matrix is projected onto the nearest positive semidefinite matrix. It only affects backends that use shots. Default: False.                                                                                                                                                                                                                      | `False`                                                                                                                            |

Example

```python
import numpy as np
from eigenkit import QuantumKernel

rng = np.random.default_rng(0)
X_train, X_test = rng.uniform(0, np.pi, (8, 4)), rng.uniform(0, np.pi, (3, 4))

qk = QuantumKernel(n_features=4, feature_map="AngleCNOT", reps=2)
K_train = qk.compute_kernel_matrix(X_train)
K_test = qk.compute_kernel_matrix(X_test, X_train)

# A custom feature map with a trainable weight.
def zz_map(qc, x, w):
    for q in range(len(x)):
        qc.ry(w[0], q)
        qc.h(q)
        qc.rz(x[q], q)
    qc.pauli_exponential([("Z", 0), ("Z", 1)], x[0])

trainable = QuantumKernel(n_features=2, feature_map=zz_map, n_weights=1)
K = trainable.compute_kernel_matrix(X_train[:, :2], weights=[0.3])
```

#### `circuit`

The compiled inversion circuit.

A backend with shots runs it for each kernel entry. It is compiled when it is first accessed.

#### `feature_map_circuit`

The compiled forward feature map `U(x; w)`.

It is compiled when it is first accessed.

#### `feature_map_spec`

The forward feature map `U(x; w)`, as a `CircuitSpec`.

#### `kernel_spec`

The inversion circuit `U(x_1; w) U(x_2; w)^dagger`, as a `CircuitSpec`.

It has `2 * n_features` inputs.

#### `n_qubits`

The number of qubits of the feature-map circuit.

#### `n_weights`

The number of trainable weights in the feature map.

#### `__sklearn_clone__()`

Return a new copy of the kernel, not compiled yet, for `sklearn.base.clone`.

`GridSearchCV`, `cross_val_score` and `Pipeline` use it. The copy has the same feature map, provider, weights, scale and `psd_projection`.

#### `compute_kernel_matrix(X, Y=None, *, weights=None)`

Return the kernel matrix between the rows of `X` and the rows of `Y`.

Parameters:

| Name      | Type      | Description                              | Default                                                                                                              |
| --------- | --------- | ---------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| `X`       | `ndarray` | The samples, of shape (n_X, n_features). | *required*                                                                                                           |
| `Y`       | \`ndarray | None\`                                   | The second set of samples, of shape (n_Y, n_features). If None, the method returns K(X, X).                          |
| `weights` | \`ndarray | None\`                                   | The feature-map weights for this evaluation. By default, they are the kernel's own weights, or zeros if it has none. |

Returns:

| Type      | Description                   |
| --------- | ----------------------------- |
| `ndarray` | An array of shape (n_X, n_Y). |

#### `draw(*, inversion=False, decimals=3)`

Return a text drawing of the feature map, or of the inversion circuit.

Parameters:

| Name        | Type   | Description                                          | Default |
| ----------- | ------ | ---------------------------------------------------- | ------- |
| `inversion` | `bool` | If True, draw U(x_1) U(x_2)^dagger instead of U(x).  | `False` |
| `decimals`  | `int`  | The number of significant digits of constant angles. | `3`     |

Returns:

| Type  | Description                                |
| ----- | ------------------------------------------ |
| `str` | The drawing, as a string of several lines. |

#### `draw_mpl(*, inversion=False, **kwargs)`

Draw the feature map, or the inversion circuit, with Matplotlib.

The inversion circuit is drawn with a measurement on every qubit, and the forward map without one. This method needs the `viz` extra.

Parameters:

| Name        | Type   | Description                                                | Default |
| ----------- | ------ | ---------------------------------------------------------- | ------- |
| `inversion` | `bool` | If True, draw U(x_1) U(x_2)^dagger instead of U(x).        | `False` |
| `**kwargs`  | `Any`  | Arguments passed to draw_mpl(), for example style="eigen". | `{}`    |

Returns:

| Type                    | Description                                            |
| ----------------------- | ------------------------------------------------------ |
| `'tuple[Figure, Axes]'` | The Matplotlib figure and axes, as the pair (fig, ax). |

#### `estimate_cost(X, Y=None)`

Return what computing `K(X, Y)` would need on the backend of this kernel.

Nothing is submitted. On a sampling backend, the inversion circuit is compiled (and transpiled, for hardware) to report its size.

Parameters:

| Name | Type      | Description                              | Default                                                                                     |
| ---- | --------- | ---------------------------------------- | ------------------------------------------------------------------------------------------- |
| `X`  | `ndarray` | The samples, of shape (n_X, n_features). | *required*                                                                                  |
| `Y`  | \`ndarray | None\`                                   | The second set of samples, of shape (n_Y, n_features), or None for the Gram matrix K(X, X). |

Returns:

| Type   | Description                                                                                                                                                                                                                                                                                            |
| ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `dict` | A dictionary with the keys method ("statevector" or "inversion"), n_circuits, n_jobs, shots and total_shots. With the "statevector" method, it also has n_state_evolutions. When the backend reports them, it also has the transpiled two-qubit gate count n_2q_gates, the depth and the backend_name. |

#### `from_json_dict(data, provider=None)`

Rebuild a kernel written by `to_json_dict()`.

Parameters:

| Name       | Type           | Description             | Default                                                                                                                                      |
| ---------- | -------------- | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| `data`     | `dict`         | The stored description. | *required*                                                                                                                                   |
| `provider` | \`ProviderSpec | None\`                  | A provider that replaces the stored one. It is needed when the stored provider held a secret, or to move a saved model onto another backend. |

Returns:

| Type              | Description         |
| ----------------- | ------------------- |
| `'QuantumKernel'` | The rebuilt kernel. |

#### `kernel_value(x1, x2, *, weights=None)`

Return the kernel value of one pair `(x1, x2)`, from the inversion circuit.

#### `retrieve_kernel_matrix(job, n_x=None, n_y=None)`

Reconnect to a kernel job submitted earlier, possibly in another session.

Parameters:

| Name  | Type  | Description | Default                                                                              |
| ----- | ----- | ----------- | ------------------------------------------------------------------------------------ |
| `job` | \`str | Path\`      | The JSON file written by KernelJob.save(), or a job id on its own.                   |
| `n_x` | \`int | None\`      | The number of rows of the matrix. It is required with a job id on its own.           |
| `n_y` | \`int | None\`      | The number of columns, for a rectangular matrix. Leave it as None for a Gram matrix. |

Returns:

| Type        | Description                                          |
| ----------- | ---------------------------------------------------- |
| `KernelJob` | A KernelJob. Call .result() on it to get the matrix. |

#### `submit_kernel_matrix(X, Y=None, *, weights=None)`

Submit `K(X, Y)` as one asynchronous job, and return its handle.

This is for the queues of a quantum processing unit (QPU), which can take minutes or hours. Print the job id of the handle or call its `save()`, and collect the matrix later with `job.result()` or, from a new session, with `retrieve_kernel_matrix()`.

Parameters:

| Name      | Type      | Description                              | Default                                                                             |
| --------- | --------- | ---------------------------------------- | ----------------------------------------------------------------------------------- |
| `X`       | `ndarray` | The samples, of shape (n_X, n_features). | *required*                                                                          |
| `Y`       | \`ndarray | None\`                                   | The second set of samples, of shape (n_Y, n_features), or None for the Gram matrix. |
| `weights` | \`ndarray | None\`                                   | The feature-map weights. By default, they are the kernel's own.                     |

Returns:

| Type        | Description  |
| ----------- | ------------ |
| `KernelJob` | A KernelJob. |

Raises:

| Type                  | Description                                                                                                             |
| --------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `NotImplementedError` | On backends without asynchronous submission, which includes every local simulator. Use compute_kernel_matrix() on them. |

#### `to_json_dict()`

Return a JSON description of the kernel, for `.eigenkit` bundles.

A custom feature map is stored as its `CircuitSpec`. Secret fields of the provider, such as access tokens, are never written.

Returns:

| Type   | Description                       |
| ------ | --------------------------------- |
| `dict` | The description, as a dictionary. |

#### `with_weights(weights)`

Return a copy of this kernel with different trainable weights.

Parameters:

| Name      | Type      | Description                             | Default    |
| --------- | --------- | --------------------------------------- | ---------- |
| `weights` | `ndarray` | The new weights, of shape (n_weights,). | *required* |

Returns:

| Type              | Description                              |
| ----------------- | ---------------------------------------- |
| `'QuantumKernel'` | A new kernel, which is not compiled yet. |

### `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). |
| `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.                                                                                   |

#### `__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. |

### `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). |
| `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.                                                                                   |

#### `__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. |

### `align_kernel(kernel, X, y, *, loss='svc', optimizer='spsa', maxiter=50, C=1.0, initial_weights=None, learning_rate=None, perturbation=0.2, random_state=0, callback=None)`

Train the weights of a kernel so that it fits the labels.

Parameters:

| Name              | Type                                      | Description                                                                                                                                                                                                                      | Default                                                                                                                                                                                                                                     |
| ----------------- | ----------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `kernel`          | `QuantumKernel`                           | A QuantumKernel whose feature map has trainable weights.                                                                                                                                                                         | *required*                                                                                                                                                                                                                                  |
| `X`               | `ndarray`                                 | The training samples, of shape (n, n_features).                                                                                                                                                                                  | *required*                                                                                                                                                                                                                                  |
| `y`               | `ndarray`                                 | The training labels, of shape (n,).                                                                                                                                                                                              | *required*                                                                                                                                                                                                                                  |
| `loss`            | \`str                                     | Callable\[[ndarray, ndarray], float\]\`                                                                                                                                                                                          | The loss to minimise. It is "svc" (the dual objective of the support vector machine, which is the loss of quantum kernel alignment), "kta" (minus the kernel-target alignment), or a callable loss(K, y) -> float.                          |
| `optimizer`       | \`str                                     | Callable\`                                                                                                                                                                                                                       | The optimiser. It can be "spsa", the default, which selects Simultaneous Perturbation Stochastic Approximation (SPSA). It can also be the name of a scipy.optimize.minimize method such as "COBYLA", or a callable optimizer(fun, x0) -> x. |
| `maxiter`         | `int`                                     | The number of SPSA iterations, or the maxiter option of SciPy.                                                                                                                                                                   | `50`                                                                                                                                                                                                                                        |
| `C`               | `float`                                   | The regularisation parameter of the support vector machine, for the "svc" loss.                                                                                                                                                  | `1.0`                                                                                                                                                                                                                                       |
| `initial_weights` | \`ndarray                                 | None\`                                                                                                                                                                                                                           | The starting point. By default, it is the kernel's own weights, or zeros if the kernel has none.                                                                                                                                            |
| `learning_rate`   | \`float                                   | None\`                                                                                                                                                                                                                           | The initial step size of SPSA. None calibrates it so that the first step moves the weights by about 2π / 10. The "svc" loss grows with the number of samples, so a fixed step that suits one dataset size can be far too large for another. |
| `perturbation`    | `float`                                   | The initial perturbation size of SPSA. The default, 0.2, is the value that qiskit-algorithms uses for its calibration. Much smaller values give estimates of the gradient that are too local to cross a flat region of the loss. | `0.2`                                                                                                                                                                                                                                       |
| `random_state`    | \`int                                     | None\`                                                                                                                                                                                                                           | The seed for the perturbation directions of SPSA.                                                                                                                                                                                           |
| `callback`        | \`Callable\[[int, ndarray, float], None\] | None\`                                                                                                                                                                                                                           | A function called as callback(iteration, weights, loss) after every SPSA iteration or SciPy evaluation.                                                                                                                                     |

Returns:

| Type                    | Description              |
| ----------------------- | ------------------------ |
| `KernelAlignmentResult` | A KernelAlignmentResult. |

Raises:

| Type         | Description                                                            |
| ------------ | ---------------------------------------------------------------------- |
| `ValueError` | If the kernel has no trainable weights, or if loss is an unknown name. |

Example

```python
import numpy as np
from eigenkit import QSVC, QuantumKernel, align_kernel

def my_map(qc, x, w):
    for q in range(len(x)):
        qc.ry(w[0], q)
        qc.rz(x[q], q)

rng = np.random.default_rng(0)
X_train = rng.uniform(0, np.pi, (8, 2))
y_train = np.array([0, 1] * 4)

kernel = QuantumKernel(n_features=2, feature_map=my_map, n_weights=1)
result = align_kernel(kernel, X_train, y_train, maxiter=20)
model = QSVC(kernel=result.kernel).fit(X_train, y_train)
```

### `get_estimator_class(name)`

Return the estimator class registered under `name`.

Parameters:

| Name   | Type  | Description                                                 | Default    |
| ------ | ----- | ----------------------------------------------------------- | ---------- |
| `name` | `str` | The class name, as stored in the metadata.json of a bundle. | *required* |

Returns:

| Type   | Description           |
| ------ | --------------------- |
| `type` | The registered class. |

Raises:

| Type         | Description                                                                                                       |
| ------------ | ----------------------------------------------------------------------------------------------------------------- |
| `ValueError` | If no class is registered under name. The module that defines the class must be imported before load() is called. |

### `kernel_target_alignment(K, y)`

Return the kernel-target alignment `<K, Y>_F / (||K||_F ||Y||_F)`.

`Y_ij = +1` when `y_i == y_j` and `-1` otherwise. For binary labels in `{-1, +1}`, this is `y y^T`.

Parameters:

| Name | Type      | Description                       | Default    |
| ---- | --------- | --------------------------------- | ---------- |
| `K`  | `ndarray` | The Gram matrix, of shape (n, n). | *required* |
| `y`  | `ndarray` | The labels, of shape (n,).        | *required* |

Returns:

| Type    | Description                                                                             |
| ------- | --------------------------------------------------------------------------------------- |
| `float` | A value in [-1, 1]. A higher value means that the kernel already separates the classes. |

### `load_model(path, *, provider=None, model=None)`

Load any saved model. The class of the model is read from `metadata.json`.

Load only bundles that you trust. A bundle can contain joblib pickles, and unpickling runs code, as with `torch.load` or `joblib.load`.

Parameters:

| Name       | Type           | Description                                                                                                                    | Default                                                                                     |
| ---------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------- |
| `path`     | \`str          | Path\`                                                                                                                         | The path to the .eigenkit bundle.                                                           |
| `provider` | \`ProviderSpec | None\`                                                                                                                         | A provider that replaces the saved one. It is passed to the load() method of the estimator. |
| `model`    | `Any`          | The nn.Module architecture, for checkpoints of GenericRegressor and GenericClassifier. It is ignored for the other estimators. | `None`                                                                                      |

Returns:

| Type                   | Description                                                         |
| ---------------------- | ------------------------------------------------------------------- |
| `BaseQuantumEstimator` | The loaded estimator, an instance of the class named in the bundle. |

### `project_psd(K)`

Return the nearest positive semidefinite matrix to a symmetric `K`.

Shot noise can make a measured Gram matrix slightly indefinite, and a support vector machine solver handles such a matrix poorly. Setting the negative eigenvalues to zero gives the nearest positive semidefinite matrix in the Frobenius norm.

Parameters:

| Name | Type      | Description                        | Default    |
| ---- | --------- | ---------------------------------- | ---------- |
| `K`  | `ndarray` | A square, symmetric kernel matrix. | *required* |

Returns:

| Type      | Description                              |
| --------- | ---------------------------------------- |
| `ndarray` | The projected matrix, of the same shape. |

### `register_estimator(cls)`

Register an estimator class under its `__name__`.

It is used as a class decorator.

Parameters:

| Name  | Type   | Description                      | Default    |
| ----- | ------ | -------------------------------- | ---------- |
| `cls` | `type` | The estimator class to register. | *required* |

Returns:

| Type   | Description                |
| ------ | -------------------------- |
| `type` | The same class, unchanged. |

### `svc_dual_loss(K, y, C=1.0)`

Return the optimal dual objective of a support vector machine (SVM).

The SVM is fitted on a precomputed kernel. This is the `"svc"` loss.

Parameters:

| Name | Type      | Description                                                    | Default    |
| ---- | --------- | -------------------------------------------------------------- | ---------- |
| `K`  | `ndarray` | The training Gram matrix, of shape (n, n).                     | *required* |
| `y`  | `ndarray` | The labels, of shape (n,). They must have exactly two classes. | *required* |
| `C`  | `float`   | The regularisation parameter of the SVM.                       | `1.0`      |

Returns:

| Type    | Description   |
| ------- | ------------- |
| `float` | The value sum |

Raises:

| Type         | Description                                                              |
| ------------ | ------------------------------------------------------------------------ |
| `ValueError` | If y does not have exactly two classes. Use loss="kta" for more classes. |
