## `eigenkit.torch.estimators`

The PyTorch estimators: differentiable hybrid quantum-classical models.

The estimator base classes compatible with scikit-learn, the quantum kernels, the support vector machines and the saving and loading of models are in a separate package, `eigenkit.qml`, which does not need PyTorch. The base classes and `load_model` are also exported here for convenience, because the hybrid estimators build on them.

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

### `CustomQuantumClassifier`

Bases: `ModelAccessMixin`, `BaseQuantumClassifier`

Classifier compatible with scikit-learn, built from your own list of blocks.

Give any sequence of blocks (`Encoding`, `Variational`, `StrongVariational`, `Reuploading`, or a custom block from `CircuitBuilder.to_block()`) and an `observables` specification, as for `Circuit`. The estimator connects them into a quantum layer followed by a classical `nn.Linear` head, and trains the whole model with the cross-entropy loss. When the number of features of the data differs from the number of inputs that the circuit expects, an `nn.Linear` adapter is added in front automatically.

Parameters:

| Name               | Type             | Description                                                                                               | Default                                                                                                   |
| ------------------ | ---------------- | --------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| `blocks`           | `list[Block]`    | The list of blocks, in order.                                                                             | *required*                                                                                                |
| `observables`      | `ObservableLike` | The observables. It takes the same options as Circuit(observables=...). Default: None (Z on every qubit). | `None`                                                                                                    |
| `provider`         | `ProviderSpec`   | The backend. Default: "numpy".                                                                            | `'numpy'`                                                                                                 |
| `in_features`      | \`int            | None\`                                                                                                    | The number of features of the data. If None, it is inferred from the first Encoding or Reuploading block. |
| `n_classes`        | \`int            | None\`                                                                                                    | The number of output classes. If None, it is inferred from the training labels.                           |
| `lr`               | `float`          | The learning rate of the optimiser.                                                                       | `0.01`                                                                                                    |
| `optimizer`        | `str`            | The name of the optimiser. The supported names are "adam", "adamw", "rmsprop" and "sgd".                  | `'adam'`                                                                                                  |
| `optimizer_kwargs` | \`dict           | None\`                                                                                                    | Extra keyword arguments passed to the optimiser.                                                          |
| `epochs`           | `int`            | The number of training epochs.                                                                            | `50`                                                                                                      |
| `batch_size`       | `int`            | The size of each mini-batch.                                                                              | `32`                                                                                                      |
| `show_progress`    | `bool`           | If True, a tqdm progress bar is shown.                                                                    | `True`                                                                                                    |
| `random_state`     | \`int            | None\`                                                                                                    | The seed.                                                                                                 |

Attributes:

| Name                 | Type | Description                                                                                  |
| -------------------- | ---- | -------------------------------------------------------------------------------------------- |
| `train_losses_`      |      | The cross-entropy loss of each epoch, set by fit().                                          |
| `validation_losses_` |      | The validation cross-entropy loss of each epoch, set by fit() when validation data is given. |

Example

```python
import numpy as np
from eigenkit import (
    CustomQuantumClassifier, Encoding, Variational, all_z,
)

rng = np.random.default_rng(0)
X_train, X_test = rng.uniform(0, np.pi, (20, 4)), rng.uniform(0, np.pi, (5, 4))
y_train = np.array([0, 1] * 10)

blocks = [
    Encoding(in_features=4, rotation="RY"),
    Variational(depth=2, rotation="RY"),
]
model = CustomQuantumClassifier(
    blocks=blocks, observables=all_z(4), epochs=10,
)
model.fit(X_train, y_train)
predictions = model.predict(X_test)
```

#### `fit(X, y, X_val=None, y_val=None)`

Fit the classifier on training data.

Parameters:

| Name    | Type      | Description                                                                                                | Default                                                                         |
| ------- | --------- | ---------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| `X`     | `ndarray` | The samples, of shape (n_samples, in_features).                                                            | *required*                                                                      |
| `y`     | `ndarray` | The class labels, of shape (n_samples,). They can be of any type that scikit-learn's LabelEncoder accepts. | *required*                                                                      |
| `X_val` | \`ndarray | None\`                                                                                                     | Optional validation samples, used to compute the validation loss of each epoch. |
| `y_val` | \`ndarray | None\`                                                                                                     | Optional validation labels, used to compute the validation loss of each epoch.  |

Returns:

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

Raises:

| Type         | Description                                                    |
| ------------ | -------------------------------------------------------------- |
| `ValueError` | If in_features is set and X does not have in_features columns. |

#### `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, with a softmax over the logits.

It returns a float32 array of shape `(n_samples, n_classes)`.

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

### `CustomQuantumRegressor`

Bases: `ModelAccessMixin`, `BaseQuantumRegressor`

Regressor compatible with scikit-learn, built from your own list of blocks.

It is the regression counterpart of CustomQuantumClassifier. It wraps your quantum `Circuit` with an `nn.Linear` head of one neuron, trained with the chosen regression loss. An `nn.Linear` input adapter is added when the number of features of the data differs from the number of inputs of the circuit.

Parameters:

| Name               | Type             | Description                                                             | Default                                                                                                   |
| ------------------ | ---------------- | ----------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| `blocks`           | `list[Block]`    | The list of blocks, in order.                                           | *required*                                                                                                |
| `observables`      | `ObservableLike` | The observables. It takes the same options as Circuit(observables=...). | `None`                                                                                                    |
| `provider`         | `ProviderSpec`   | The backend. Default: "numpy".                                          | `'numpy'`                                                                                                 |
| `in_features`      | \`int            | None\`                                                                  | The number of features of the data. If None, it is inferred from the first Encoding or Reuploading block. |
| `lr`               | `float`          | The learning rate of the optimiser.                                     | `0.01`                                                                                                    |
| `optimizer`        | `str`            | The name of the optimiser: "adam", "adamw", "rmsprop" or "sgd".         | `'adam'`                                                                                                  |
| `optimizer_kwargs` | \`dict           | None\`                                                                  | Extra keyword arguments passed to the optimiser.                                                          |
| `epochs`           | `int`            | The number of training epochs.                                          | `50`                                                                                                      |
| `batch_size`       | `int`            | The size of each mini-batch.                                            | `32`                                                                                                      |
| `loss_func`        | `str`            | The regression loss: "mse", "mae" or "huber".                           | `'mse'`                                                                                                   |
| `huber_delta`      | `float`          | The threshold of the Huber loss.                                        | `1.0`                                                                                                     |
| `huber_reduction`  | `str`            | The reduction of the Huber loss: "mean" or "sum".                       | `'mean'`                                                                                                  |
| `show_progress`    | `bool`           | If True, a tqdm progress bar is shown.                                  | `True`                                                                                                    |
| `random_state`     | \`int            | None\`                                                                  | The seed.                                                                                                 |

Attributes:

| Name                 | Type | Description                                                                    |
| -------------------- | ---- | ------------------------------------------------------------------------------ |
| `train_losses_`      |      | The training loss of each epoch, set by fit().                                 |
| `validation_losses_` |      | The validation loss of each epoch, set by fit() when validation data is given. |

#### `fit(X, y, X_val=None, y_val=None)`

Fit the regressor on training data.

Parameters:

| Name    | Type      | Description                                     | Default                                                                         |
| ------- | --------- | ----------------------------------------------- | ------------------------------------------------------------------------------- |
| `X`     | `ndarray` | The samples, of shape (n_samples, in_features). | *required*                                                                      |
| `y`     | `ndarray` | The real-valued targets, of shape (n_samples,). | *required*                                                                      |
| `X_val` | \`ndarray | None\`                                          | Optional validation samples, used to compute the validation loss of each epoch. |
| `y_val` | \`ndarray | None\`                                          | Optional validation targets, used to compute the validation loss of each epoch. |

Returns:

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

#### `predict(X)`

Predict the continuous targets of `X`. It returns a float32 array.

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

### `GenericClassifier`

Bases: `ModelAccessMixin`, `BaseQuantumClassifier`

Classifier compatible with scikit-learn that wraps any `torch.nn.Module`.

You supply a complete model whose `forward(X)` accepts a float32 tensor of shape `(batch, in_features)` and returns logits of shape `(batch, n_classes)`. For sequence models (for example a long short-term memory network), set `sequence_input=True`. The wrapper then passes a 3-D tensor of shape `(batch, seq_len, in_features)` as it is, instead of flattening it.

The wrapper trains with `CrossEntropyLoss` and encodes the labels automatically.

Parameters:

| Name               | Type     | Description                                                                                                                                                                               | Default                                                                                                                                                                                |
| ------------------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `model`            | `Module` | Any nn.Module. It must include its own classification head, which gives n_classes logits.                                                                                                 | *required*                                                                                                                                                                             |
| `in_features`      | \`int    | None\`                                                                                                                                                                                    | The expected number of features at each time step (the last dimension of the input). When it is given, fit raises an error if X.shape[-1] does not match. Pass None to skip the check. |
| `sequence_input`   | `bool`   | If True, the estimator tells QuantumTimeSeriesClassifier that it expects a 3-D tensor of shape (batch, seq_len, in_features), so the windowed data must not be flattened. Default: False. | `False`                                                                                                                                                                                |
| `n_classes`        | \`int    | None\`                                                                                                                                                                                    | The number of output classes. If None, it is inferred from the training labels. If it is given, it must equal the number of distinct labels in y.                                      |
| `lr`               | `float`  | The learning rate of the optimiser.                                                                                                                                                       | `0.01`                                                                                                                                                                                 |
| `optimizer`        | `str`    | The name of the optimiser: "adam", "adamw", "rmsprop" or "sgd".                                                                                                                           | `'adam'`                                                                                                                                                                               |
| `optimizer_kwargs` | \`dict   | None\`                                                                                                                                                                                    | Extra keyword arguments passed to the constructor of the optimiser.                                                                                                                    |
| `epochs`           | `int`    | The number of full passes over the training data.                                                                                                                                         | `50`                                                                                                                                                                                   |
| `batch_size`       | `int`    | The size of each mini-batch.                                                                                                                                                              | `32`                                                                                                                                                                                   |
| `show_progress`    | `bool`   | If True, a tqdm progress bar is shown during training.                                                                                                                                    | `True`                                                                                                                                                                                 |
| `random_state`     | \`int    | None\`                                                                                                                                                                                    | The seed, applied before the first training step.                                                                                                                                      |

Attributes:

| Name                 | Type | Description                                                                                  |
| -------------------- | ---- | -------------------------------------------------------------------------------------------- |
| `train_losses_`      |      | The average cross-entropy loss of each epoch, set by fit().                                  |
| `validation_losses_` |      | The validation cross-entropy loss of each epoch, set by fit() when validation data is given. |

Example

```python
import numpy as np
from torch import nn
from eigenkit.torch.estimators import GenericClassifier
from eigenkit.torch.layers import Basic

rng = np.random.default_rng(0)
X_train, X_test = rng.normal(size=(30, 4)), rng.normal(size=(5, 4))
y_train = np.arange(30) % 3

# A plain PyTorch network, with a head for 3 classes.
net = nn.Sequential(nn.Linear(4, 8), nn.ReLU(), nn.Linear(8, 3))
clf = GenericClassifier(model=net, in_features=4, epochs=20)
clf.fit(X_train, y_train)
labels = clf.predict(X_test)
probs  = clf.predict_proba(X_test)

# A quantum layer with a classification head.
q = Basic(n_qubits=4, depth=2)
hybrid = nn.Sequential(q, nn.Linear(4, 3))
clf2 = GenericClassifier(model=hybrid, in_features=4)
```

#### `accepts_sequence_input`

Whether the model expects a 3-D input `(batch, seq_len, features)`.

#### `fit(X, y, X_val=None, y_val=None)`

Fit the model on training data.

Parameters:

| Name    | Type      | Description                                                                                                                    | Default                                                                         |
| ------- | --------- | ------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------- |
| `X`     | `ndarray` | The samples, of shape (n_samples, in_features) for flat models, or (n_samples, seq_len, in_features) when sequence_input=True. | *required*                                                                      |
| `y`     | `ndarray` | The class labels, of shape (n_samples,).                                                                                       | *required*                                                                      |
| `X_val` | \`ndarray | None\`                                                                                                                         | Optional validation samples, used to compute the validation loss of each epoch. |
| `y_val` | \`ndarray | None\`                                                                                                                         | Optional validation labels, used to compute the validation loss of each epoch.  |

Returns:

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

Raises:

| Type         | Description                                                                                                                                                                  |
| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ValueError` | If X.shape[-1] or X_val.shape[-1] differs from in_features, if n_classes differs from the number of distinct labels, or if the model does not give one logit for each class. |

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

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

The architecture of the model cannot be rebuilt automatically, so you must give it. The saved weights and the label encoder are restored automatically.

Parameters:

| Name       | Type           | Description | Default                                                                                                                                                                            |
| ---------- | -------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `path`     | \`str          | Path\`      | The path of the .eigenkit bundle.                                                                                                                                                  |
| `model`    | \`Module       | None\`      | An nn.Module with the same architecture as the one used in training. Its weights are not changed: the saved weights go into a deep copy, which is model\_ of the loaded estimator. |
| `provider` | \`ProviderSpec | None\`      | It is ignored. It is kept so that the signature matches the other estimators.                                                                                                      |

Returns:

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

Raises:

| Type                 | Description                            |
| -------------------- | -------------------------------------- |
| `SerializationError` | If model is not given.                 |
| `TypeError`          | If the bundle holds a different class. |

Example

```python
from torch import nn
from eigenkit.torch.estimators import GenericClassifier

net = nn.Sequential(nn.Linear(4, 8), nn.ReLU(), nn.Linear(8, 3))
clf = GenericClassifier.load("checkpoint.eigenkit", model=net)
```

#### `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, with a softmax over the logits.

It returns a float32 array of shape `(n_samples, n_classes)`.

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

### `GenericRegressor`

Bases: `ModelAccessMixin`, `BaseQuantumRegressor`

Regressor compatible with scikit-learn that wraps any `torch.nn.Module`.

You supply a complete model whose `forward(X)` accepts a float32 tensor of shape `(batch, in_features)` and returns a tensor of shape `(batch, 1)` or `(batch,)`. For sequence models (for example a long short-term memory network), set `sequence_input=True`. The wrapper then passes a 3-D tensor of shape `(batch, seq_len, in_features)` as it is, instead of flattening it.

The wrapper trains the model with the standard `train_loop()` of the library, and returns the predictions as a float32 NumPy array.

Parameters:

| Name               | Type     | Description                                                                                                                                                                              | Default                                                                                                                                                                                |
| ------------------ | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `model`            | `Module` | Any nn.Module. It must include its own output head.                                                                                                                                      | *required*                                                                                                                                                                             |
| `in_features`      | \`int    | None\`                                                                                                                                                                                   | The expected number of features at each time step (the last dimension of the input). When it is given, fit raises an error if X.shape[-1] does not match. Pass None to skip the check. |
| `sequence_input`   | `bool`   | If True, the estimator tells QuantumTimeSeriesRegressor that it expects a 3-D tensor of shape (batch, seq_len, in_features), so the windowed data must not be flattened. Default: False. | `False`                                                                                                                                                                                |
| `loss_func`        | \`str    | Callable\`                                                                                                                                                                               | The loss: "mse", "mae", "huber", or any callable (output, target) -> scalar_tensor.                                                                                                    |
| `huber_delta`      | `float`  | The threshold of the Huber loss. It is ignored for the other losses.                                                                                                                     | `1.0`                                                                                                                                                                                  |
| `huber_reduction`  | `str`    | The reduction of the Huber loss: "mean" or "sum".                                                                                                                                        | `'mean'`                                                                                                                                                                               |
| `lr`               | `float`  | The learning rate of the optimiser.                                                                                                                                                      | `0.01`                                                                                                                                                                                 |
| `optimizer`        | `str`    | The name of the optimiser: "adam", "adamw", "rmsprop" or "sgd".                                                                                                                          | `'adam'`                                                                                                                                                                               |
| `optimizer_kwargs` | \`dict   | None\`                                                                                                                                                                                   | Extra keyword arguments passed to the constructor of the optimiser.                                                                                                                    |
| `epochs`           | `int`    | The number of full passes over the training data.                                                                                                                                        | `50`                                                                                                                                                                                   |
| `batch_size`       | `int`    | The size of each mini-batch.                                                                                                                                                             | `32`                                                                                                                                                                                   |
| `show_progress`    | `bool`   | If True, a tqdm progress bar is shown during training.                                                                                                                                   | `True`                                                                                                                                                                                 |
| `random_state`     | \`int    | None\`                                                                                                                                                                                   | The seed, applied before the first training step.                                                                                                                                      |

Attributes:

| Name                 | Type | Description                                                                                |
| -------------------- | ---- | ------------------------------------------------------------------------------------------ |
| `train_losses_`      |      | The list of the average loss of each epoch, set by fit().                                  |
| `validation_losses_` |      | The list of the validation loss of each epoch, set by fit() when validation data is given. |

Example

```python
import numpy as np
from torch import nn
from eigenkit.torch.estimators import GenericRegressor
from eigenkit.torch.layers import Basic

rng = np.random.default_rng(0)
X_train, X_test = rng.normal(size=(30, 8)), rng.normal(size=(5, 8))
y_train = X_train.sum(axis=1)

# A plain PyTorch network.
net = nn.Sequential(nn.Linear(8, 16), nn.ReLU(), nn.Linear(16, 1))
reg = GenericRegressor(model=net, in_features=8, epochs=30)
reg.fit(X_train, y_train)
preds = reg.predict(X_test)

# A quantum layer with a classical head.
q = Basic(n_qubits=4, depth=2)
hybrid = nn.Sequential(q, nn.Linear(4, 1))
reg2 = GenericRegressor(model=hybrid, in_features=4)
```

#### `accepts_sequence_input`

Whether the model expects a 3-D input `(batch, seq_len, features)`.

#### `fit(X, y, X_val=None, y_val=None)`

Fit the model on training data.

Parameters:

| Name    | Type      | Description                                                                                                                    | Default                                                                         |
| ------- | --------- | ------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------- |
| `X`     | `ndarray` | The samples, of shape (n_samples, in_features) for flat models, or (n_samples, seq_len, in_features) when sequence_input=True. | *required*                                                                      |
| `y`     | `ndarray` | The real-valued targets, of shape (n_samples,).                                                                                | *required*                                                                      |
| `X_val` | \`ndarray | None\`                                                                                                                         | Optional validation samples, used to compute the validation loss of each epoch. |
| `y_val` | \`ndarray | None\`                                                                                                                         | Optional validation targets, used to compute the validation loss of each epoch. |

Returns:

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

Raises:

| Type         | Description                                                                                                                           |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------- |
| `ValueError` | If X.shape[-1] or X_val.shape[-1] differs from in_features, or if the output of the model does not have shape (batch,) or (batch, 1). |

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

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

The architecture of the model cannot be rebuilt automatically, so you must give it. The saved weights are loaded into a copy of it.

Parameters:

| Name       | Type           | Description | Default                                                                                                                                                                            |
| ---------- | -------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `path`     | \`str          | Path\`      | The path of the .eigenkit bundle.                                                                                                                                                  |
| `model`    | \`Module       | None\`      | An nn.Module with the same architecture as the one used in training. Its weights are not changed: the saved weights go into a deep copy, which is model\_ of the loaded estimator. |
| `provider` | \`ProviderSpec | None\`      | It is ignored. It is kept so that the signature matches the other estimators.                                                                                                      |

Returns:

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

Raises:

| Type                 | Description                            |
| -------------------- | -------------------------------------- |
| `SerializationError` | If model is not given.                 |
| `TypeError`          | If the bundle holds a different class. |

Example

```python
from torch import nn
from eigenkit.torch.estimators import GenericRegressor

net = nn.Sequential(nn.Linear(8, 16), nn.ReLU(), nn.Linear(16, 1))
reg = GenericRegressor.load("checkpoint.eigenkit", model=net)
```

#### `predict(X)`

Predict the continuous targets of `X`. It returns a float32 array.

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

### `HQNC`

Bases: `ModelAccessMixin`, `BaseQuantumClassifier`

Hybrid quantum neural network classifier (HQNC).

The model is `MinMaxNorm -> quantum_layer -> nn.Linear(q_out, n_classes)`. If the number of features of the data differs from the resolved input size of the quantum layer (derived from `in_quantum`, `n_qubits` and `layer_type`), an `nn.Linear` adapter is added in front automatically.

Parameters:

| Name               | Type                         | Description                                                                              | Default                                                                                                                                                                                |
| ------------------ | ---------------------------- | ---------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `in_features`      | `int`                        | The number of input features.                                                            | *required*                                                                                                                                                                             |
| `in_quantum`       | \`int                        | None\`                                                                                   | The number of inputs that the quantum layer expects. It defaults to in_features.                                                                                                       |
| `n_qubits`         | \`int                        | None\`                                                                                   | The number of qubits in each circuit. It defaults to in_quantum.                                                                                                                       |
| `depth`            | `int`                        | The number of repetitions of the variational layer.                                      | `2`                                                                                                                                                                                    |
| `n_classes`        | \`int                        | None\`                                                                                   | The number of output classes. If None, it is inferred from the training labels.                                                                                                        |
| `layer_type`       | `str`                        | The quantum layer. It is one of "Basic", "Reupload" and "Ensemble".                      | `'Basic'`                                                                                                                                                                              |
| `enc_rotation`     | \`Literal['RX', 'RY', 'RZ']  | None\`                                                                                   | The encoding rotation: "RX", "RY", "RZ" or None.                                                                                                                                       |
| `var_rotation`     | \`Literal['RX', 'RY', 'RZ']  | None\`                                                                                   | The variational rotation of the "basic" ansatz: "RX", "RY", "RZ" or None.                                                                                                              |
| `variational_type` | \`Literal['basic', 'strong'] | None\`                                                                                   | The type of variational block: "basic", "strong" or None.                                                                                                                              |
| `basis`            | \`Literal['X', 'Y', 'Z']     | None\`                                                                                   | The measurement basis: "X", "Y", "Z" or None. For these four arguments, None means "use the default of the layer named by layer_type", which is where each of these values is decided. |
| `provider`         | `ProviderSpec`               | The backend. It takes the same options as Circuit(provider=...).                         | `'numpy'`                                                                                                                                                                              |
| `lr`               | `float`                      | The learning rate of the optimiser.                                                      | `0.01`                                                                                                                                                                                 |
| `optimizer`        | `str`                        | The name of the optimiser. The supported names are "adam", "adamw", "rmsprop" and "sgd". | `'adam'`                                                                                                                                                                               |
| `optimizer_kwargs` | \`dict                       | None\`                                                                                   | Extra keyword arguments passed to the optimiser.                                                                                                                                       |
| `epochs`           | `int`                        | The number of training epochs.                                                           | `50`                                                                                                                                                                                   |
| `batch_size`       | `int`                        | The size of each mini-batch.                                                             | `32`                                                                                                                                                                                   |
| `show_progress`    | `bool`                       | If True, a tqdm progress bar is shown.                                                   | `True`                                                                                                                                                                                 |
| `random_state`     | \`int                        | None\`                                                                                   | The seed for the initialisation of the weights.                                                                                                                                        |

Attributes:

| Name                 | Type | Description                                                                                  |
| -------------------- | ---- | -------------------------------------------------------------------------------------------- |
| `train_losses_`      |      | The average cross-entropy loss of each epoch, set by fit().                                  |
| `validation_losses_` |      | The validation cross-entropy loss of each epoch, set by fit() when validation data is given. |

Example

```python
import numpy as np
from eigenkit.torch.estimators import HQNC

rng = np.random.default_rng(0)
X_train, X_test = rng.normal(size=(30, 4)), rng.normal(size=(5, 4))
y_train = np.arange(30) % 3

model = HQNC(in_features=4, depth=2, n_classes=3)
model.fit(X_train, y_train)
predictions = model.predict(X_test)
```

#### `__init__(in_features, in_quantum=None, n_qubits=None, depth=2, n_classes=None, layer_type='Basic', enc_rotation=None, var_rotation=None, variational_type=None, basis=None, provider='numpy', lr=0.01, optimizer='adam', optimizer_kwargs=None, epochs=50, batch_size=32, show_progress=True, random_state=42)`

Initialise a hybrid quantum neural network classifier (HQNC).

Parameters:

| Name               | Type                         | Description                                                                              | Default                                                                                                                                                                                |
| ------------------ | ---------------------------- | ---------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `in_features`      | `int`                        | The number of input features that fit() expects.                                         | *required*                                                                                                                                                                             |
| `in_quantum`       | \`int                        | None\`                                                                                   | The number of inputs that the quantum layer expects. It defaults to in_features.                                                                                                       |
| `n_qubits`         | \`int                        | None\`                                                                                   | The number of qubits in each circuit. It defaults to in_quantum.                                                                                                                       |
| `depth`            | `int`                        | The number of repetitions of the variational layer.                                      | `2`                                                                                                                                                                                    |
| `n_classes`        | \`int                        | None\`                                                                                   | The number of output classes. If None, it is inferred from the labels passed to fit().                                                                                                 |
| `layer_type`       | `str`                        | The family of the quantum layer. It is one of "Basic", "Reupload" and "Ensemble".        | `'Basic'`                                                                                                                                                                              |
| `enc_rotation`     | \`Literal['RX', 'RY', 'RZ']  | None\`                                                                                   | The encoding rotation: "RX", "RY", "RZ" or None.                                                                                                                                       |
| `var_rotation`     | \`Literal['RX', 'RY', 'RZ']  | None\`                                                                                   | The variational rotation of the basic ansatz: "RX", "RY", "RZ" or None.                                                                                                                |
| `variational_type` | \`Literal['basic', 'strong'] | None\`                                                                                   | The type of variational block: "basic", "strong" or None.                                                                                                                              |
| `basis`            | \`Literal['X', 'Y', 'Z']     | None\`                                                                                   | The measurement basis: "X", "Y", "Z" or None. For these four arguments, None means "use the default of the layer named by layer_type", which is where each of these values is decided. |
| `provider`         | `ProviderSpec`               | The backend. It takes the same options as Circuit(provider=...).                         | `'numpy'`                                                                                                                                                                              |
| `lr`               | `float`                      | The learning rate of the optimiser.                                                      | `0.01`                                                                                                                                                                                 |
| `optimizer`        | `str`                        | The name of the optimiser. The supported names are "adam", "adamw", "rmsprop" and "sgd". | `'adam'`                                                                                                                                                                               |
| `optimizer_kwargs` | \`dict                       | None\`                                                                                   | Extra keyword arguments passed to the optimiser.                                                                                                                                       |
| `epochs`           | `int`                        | The number of training epochs.                                                           | `50`                                                                                                                                                                                   |
| `batch_size`       | `int`                        | The size of each mini-batch.                                                             | `32`                                                                                                                                                                                   |
| `show_progress`    | `bool`                       | If True, a tqdm progress bar is shown during training.                                   | `True`                                                                                                                                                                                 |
| `random_state`     | \`int                        | None\`                                                                                   | The seed for the initialisation of the weights.                                                                                                                                        |

#### `fit(X, y, X_val=None, y_val=None)`

Fit the classifier on training data.

Parameters:

| Name    | Type      | Description                                                                                                | Default                                                                         |
| ------- | --------- | ---------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| `X`     | `ndarray` | The samples, of shape (n_samples, in_features).                                                            | *required*                                                                      |
| `y`     | `ndarray` | The class labels, of shape (n_samples,). They can be of any type that scikit-learn's LabelEncoder accepts. | *required*                                                                      |
| `X_val` | \`ndarray | None\`                                                                                                     | Optional validation samples, used to compute the validation loss of each epoch. |
| `y_val` | \`ndarray | None\`                                                                                                     | Optional validation labels, used to compute the validation loss of each epoch.  |

Returns:

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

Raises:

| Type         | Description                             |
| ------------ | --------------------------------------- |
| `ValueError` | If X does not have in_features columns. |

#### `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, with a softmax over the logits.

It returns a float32 array of shape `(n_samples, n_classes)`. Each row sums to `1`.

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

### `HQNR`

Bases: `ModelAccessMixin`, `BaseQuantumRegressor`

Hybrid quantum neural network regressor (HQNR).

It is the regression counterpart of `HQNC`. The model is `MinMaxNorm -> quantum_layer -> nn.Linear(q_out, 1)`. If the number of features of the data differs from the resolved input size of the quantum layer, an `nn.Linear` adapter is added in front automatically.

Parameters:

| Name               | Type                         | Description                                                                              | Default                                                                                                                                                                                |
| ------------------ | ---------------------------- | ---------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `in_features`      | `int`                        | The number of input features.                                                            | *required*                                                                                                                                                                             |
| `in_quantum`       | \`int                        | None\`                                                                                   | The number of inputs that the quantum layer expects. It defaults to in_features.                                                                                                       |
| `n_qubits`         | \`int                        | None\`                                                                                   | The number of qubits in each circuit. It defaults to in_quantum.                                                                                                                       |
| `depth`            | `int`                        | The number of repetitions of the variational layer.                                      | `2`                                                                                                                                                                                    |
| `layer_type`       | `str`                        | The quantum layer. It is one of "Basic", "Reupload" and "Ensemble".                      | `'Basic'`                                                                                                                                                                              |
| `enc_rotation`     | \`Literal['RX', 'RY', 'RZ']  | None\`                                                                                   | The encoding rotation: "RX", "RY", "RZ" or None.                                                                                                                                       |
| `var_rotation`     | \`Literal['RX', 'RY', 'RZ']  | None\`                                                                                   | The variational rotation of the "basic" ansatz: "RX", "RY", "RZ" or None.                                                                                                              |
| `variational_type` | \`Literal['basic', 'strong'] | None\`                                                                                   | The type of variational block: "basic", "strong" or None.                                                                                                                              |
| `basis`            | \`Literal['X', 'Y', 'Z']     | None\`                                                                                   | The measurement basis: "X", "Y", "Z" or None. For these four arguments, None means "use the default of the layer named by layer_type", which is where each of these values is decided. |
| `provider`         | `ProviderSpec`               | The backend. It takes the same options as Circuit(provider=...).                         | `'numpy'`                                                                                                                                                                              |
| `lr`               | `float`                      | The learning rate of the optimiser.                                                      | `0.01`                                                                                                                                                                                 |
| `optimizer`        | `str`                        | The name of the optimiser. The supported names are "adam", "adamw", "rmsprop" and "sgd". | `'adam'`                                                                                                                                                                               |
| `optimizer_kwargs` | \`dict                       | None\`                                                                                   | Extra keyword arguments passed to the optimiser.                                                                                                                                       |
| `epochs`           | `int`                        | The number of training epochs.                                                           | `50`                                                                                                                                                                                   |
| `batch_size`       | `int`                        | The size of each mini-batch.                                                             | `32`                                                                                                                                                                                   |
| `show_progress`    | `bool`                       | If True, a tqdm progress bar is shown.                                                   | `True`                                                                                                                                                                                 |
| `loss_func`        | `str`                        | The regression loss: "mse", "mae" or "huber".                                            | `'mse'`                                                                                                                                                                                |
| `huber_delta`      | `float`                      | The threshold of the Huber loss.                                                         | `1.0`                                                                                                                                                                                  |
| `huber_reduction`  | `str`                        | The reduction of the Huber loss: "mean" or "sum".                                        | `'mean'`                                                                                                                                                                               |
| `random_state`     | \`int                        | None\`                                                                                   | The seed.                                                                                                                                                                              |

Attributes:

| Name                 | Type | Description                                                                    |
| -------------------- | ---- | ------------------------------------------------------------------------------ |
| `train_losses_`      |      | The training loss of each epoch, set by fit().                                 |
| `validation_losses_` |      | The validation loss of each epoch, set by fit() when validation data is given. |

Example

```python
import numpy as np
from eigenkit.torch.estimators import HQNR

rng = np.random.default_rng(0)
X_train, X_test = rng.normal(size=(30, 8)), rng.normal(size=(5, 8))
y_train = X_train.sum(axis=1)

model = HQNR(in_features=8, depth=2)
model.fit(X_train, y_train)
predictions = model.predict(X_test)
```

#### `fit(X, y, X_val=None, y_val=None)`

Fit the regressor on training data.

Parameters:

| Name    | Type      | Description                                     | Default                                                                         |
| ------- | --------- | ----------------------------------------------- | ------------------------------------------------------------------------------- |
| `X`     | `ndarray` | The samples, of shape (n_samples, in_features). | *required*                                                                      |
| `y`     | `ndarray` | The real-valued targets, of shape (n_samples,). | *required*                                                                      |
| `X_val` | \`ndarray | None\`                                          | Optional validation samples, used to compute the validation loss of each epoch. |
| `y_val` | \`ndarray | None\`                                          | Optional validation targets, used to compute the validation loss of each epoch. |

Returns:

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

Raises:

| Type         | Description                             |
| ------------ | --------------------------------------- |
| `ValueError` | If X does not have in_features columns. |

#### `predict(X)`

Predict the continuous targets of `X`. It returns a float32 array.

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

### `QuantumTimeSeriesClassifier`

Bases: `BaseQuantumClassifier`

Supervised wrapper for time-series classification.

It builds sliding windows over the rows of `X` (one row for each time step). Each window has shape `(in_size, n_features)`. Stacked along the batch axis, they form an array of shape `(n_windows, in_size, n_features)`, ready for sequence models such as `RecurrentHQNC`. When `autoregressive=True`, an extra last channel holds the target value of each timestep, so each step has `n_features + 1` values. For a window that starts at row `i`, the classification target is `y[i + in_size - 1 + shift]`.

Parameters:

| Name             | Type   | Description                                                                                                                                                                                                                                                   | Default                                                               |
| ---------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------- |
| `estimator`      | `Any`  | The inner estimator. It must have fit(X, y) and predict(X).                                                                                                                                                                                                   | *required*                                                            |
| `in_size`        | \`int  | None\`                                                                                                                                                                                                                                                        | The length of the window, in time steps. If None, it is treated as 1. |
| `shift`          | `int`  | How many steps after the last timestep of the window the target is. It must be zero or more. shift=0 predicts the target at the last timestep.                                                                                                                | `1`                                                                   |
| `autoregressive` | `bool` | If True, a channel with y[i + k] for k = 0, ..., in_size - 1 is added to each window. With shift=0, this leaks the target into the inputs, and a UserWarning is raised. When autoregressive=True, predict() and predict_proba() require a y_history argument. | `False`                                                               |

Attributes:

| Name            | Type | Description                                                                                                                                                       |
| --------------- | ---- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `classes_`      |      | The distinct class labels seen by fit().                                                                                                                          |
| `train_losses_` |      | The training loss of each epoch, copied from the inner estimator if it has train_losses\_ (HQNC, RecurrentHQNC), and an empty list otherwise. It is set by fit(). |

Example

```python
import numpy as np
from eigenkit.torch.estimators import (
    QuantumTimeSeriesClassifier, RecurrentHQNC,
)

rng = np.random.default_rng(0)
X_train, X_test = rng.normal(size=(40, 4)), rng.normal(size=(15, 4))
y_train = np.arange(40) % 2

inner = RecurrentHQNC(seq_feature_dim=4, rnn_hidden_dim=4, depth=2)
model = QuantumTimeSeriesClassifier(estimator=inner, in_size=10, shift=1)
model.fit(X_train, y_train)
predictions = model.predict(X_test)
```

#### `output`

The number of classes.

After `fit()`, it is the number of classes seen. Before, it is the `n_classes` of the inner estimator, or None if it has none.

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

Fit the inner classifier on the windows and their labels.

Parameters:

| Name | Type      | Description                                                     | Default    |
| ---- | --------- | --------------------------------------------------------------- | ---------- |
| `X`  | `ndarray` | The features in time order, of shape (n_timesteps, n_features). | *required* |
| `y`  | `ndarray` | The class labels, of shape (n_timesteps,) or (n_timesteps, 1).  | *required* |

Returns:

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

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

Load a fitted time-series classifier wrapper from a `.eigenkit` bundle.

Parameters:

| Name       | Type           | Description                                                                                                                                 | Default                                                                                |
| ---------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
| `path`     | \`str          | Path\`                                                                                                                                      | The path of the .eigenkit bundle.                                                      |
| `provider` | \`ProviderSpec | None\`                                                                                                                                      | An optional provider that replaces the saved one. It is passed to the inner estimator. |
| `model`    | `Any`          | The nn.Module architecture, required when the inner estimator is GenericClassifier. Pass the same architecture as the one used in training. | `None`                                                                                 |

Returns:

| Type                            | Description         |
| ------------------------------- | ------------------- |
| `'QuantumTimeSeriesClassifier'` | The loaded wrapper. |

Raises:

| Type                 | Description                                             |
| -------------------- | ------------------------------------------------------- |
| `TypeError`          | If the bundle holds a different class.                  |
| `SerializationError` | If the inner estimator needs model and it is not given. |

#### `predict(X, y_history=None)`

Predict the class labels of the windows of `X`.

Parameters:

| Name        | Type      | Description                                                     | Default                                                                                                                                                                                                                                                             |
| ----------- | --------- | --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `X`         | `ndarray` | The features in time order, of shape (n_timesteps, n_features). | *required*                                                                                                                                                                                                                                                          |
| `y_history` | \`ndarray | None\`                                                          | The past class labels, aligned with the rows of X. They fill the autoregressive channel of each window. It is required when autoregressive=True, and it must have at least n_timesteps - shift elements (that is, n_timesteps - in_size - shift + 1 + in_size - 1). |

Returns:

| Type      | Description                                                          |
| --------- | -------------------------------------------------------------------- |
| `ndarray` | The predicted labels, of shape (n_timesteps - in_size - shift + 1,). |

#### `predict_proba(X, y_history=None)`

Return the estimated class probabilities of the windows of `X`.

Parameters:

| Name        | Type      | Description                                                     | Default                                                                        |
| ----------- | --------- | --------------------------------------------------------------- | ------------------------------------------------------------------------------ |
| `X`         | `ndarray` | The features in time order, of shape (n_timesteps, n_features). | *required*                                                                     |
| `y_history` | \`ndarray | None\`                                                          | The past class labels. It is required when autoregressive=True. See predict(). |

Returns:

| Type      | Description                                      |
| --------- | ------------------------------------------------ |
| `ndarray` | A float32 array of shape (n_windows, n_classes). |

Raises:

| Type                  | Description                                  |
| --------------------- | -------------------------------------------- |
| `NotImplementedError` | If the inner estimator has no predict_proba. |

#### `score(X, y, *, return_dict=False)`

```python
score(
    X: np.ndarray,
    y: np.ndarray,
    *,
    return_dict: Literal[False] = False,
) -> float
```

```python
score(
    X: np.ndarray,
    y: np.ndarray,
    *,
    return_dict: Literal[True],
) -> dict[str, float]
```

Score the estimator on the window targets, aligned as in `predict`.

By default, it returns the accuracy, for compatibility with scikit-learn (`cross_val_score`, `GridSearchCV`). Pass `return_dict=True` to get a dict with `accuracy`, `f1` (weighted), `precision` (weighted) and `recall` (weighted).

Note

When `autoregressive=True`, the true labels `y` are used as the autoregressive history channel (teacher forcing). This is the correct behaviour for held-out evaluation, but it means that `score` cannot be called without the true labels of all the timesteps in `X`.

Parameters:

| Name          | Type      | Description                                                                                    | Default    |
| ------------- | --------- | ---------------------------------------------------------------------------------------------- | ---------- |
| `X`           | `ndarray` | The samples, of shape (n_samples, n_features). The windows are built inside.                   | *required* |
| `y`           | `ndarray` | One target for each sample, of shape (n_samples,).                                             | *required* |
| `return_dict` | `bool`    | If True, every metric is returned in a dict, instead of the single main score. Default: False. | `False`    |

Returns:

| Type    | Description        |
| ------- | ------------------ |
| \`float | dict[str, float]\` |

### `QuantumTimeSeriesRegressor`

Bases: `BaseQuantumRegressor`

Supervised wrapper with a single output, for time-series regression.

It builds sliding windows over the rows of `X` (one row for each time step). Each window has shape `(in_size, n_features)`. Stacked along the batch axis, they form an array of shape `(n_windows, in_size, n_features)`, ready for sequence models such as `torch.nn.LSTM`. When `autoregressive=True`, an extra last channel holds the target value of each timestep, so each step has `n_features + 1` values. For a window that starts at row `i`, the regression target is `y[i + in_size - 1 + shift]`.

Parameters:

| Name             | Type   | Description                                                                                                                                                             | Default                                                               |
| ---------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------- |
| `estimator`      | `Any`  | The inner estimator. It must have fit(X, y) and predict(X).                                                                                                             | *required*                                                            |
| `in_size`        | \`int  | None\`                                                                                                                                                                  | The length of the window, in time steps. If None, it is treated as 1. |
| `shift`          | `int`  | How many steps after the last timestep of the window the target is. It must be zero or more. shift=0 predicts the target at the last timestep.                          | `1`                                                                   |
| `autoregressive` | `bool` | If True, a channel with y[i + k] for k = 0, ..., in_size - 1 is added to each window. With shift=0, this leaks the target into the inputs, and a UserWarning is raised. | `False`                                                               |

Attributes:

| Name            | Type | Description                                                                                                                                                       |
| --------------- | ---- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `train_losses_` |      | The training loss of each epoch, copied from the inner estimator if it has train_losses\_ (HQNR, RecurrentHQNR), and an empty list otherwise. It is set by fit(). |

Example

```python
import numpy as np
from eigenkit.torch.estimators import QuantumTimeSeriesRegressor, RecurrentHQNR

rng = np.random.default_rng(0)
X_train, X_test = rng.normal(size=(40, 3)), rng.normal(size=(15, 3))
y_train = np.sin(np.arange(40) / 4)

# The inner estimator should accept X of shape (batch, in_size, n_features).
inner = RecurrentHQNR(seq_feature_dim=3, rnn_hidden_dim=4, epochs=5)
model = QuantumTimeSeriesRegressor(estimator=inner, in_size=10, shift=1)
model.fit(X_train, y_train)
predictions = model.predict(X_test)
```

#### `output`

The number of outputs, which is always `1`.

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

Fit the inner estimator on the windows and their targets.

Parameters:

| Name | Type      | Description                                                     | Default    |
| ---- | --------- | --------------------------------------------------------------- | ---------- |
| `X`  | `ndarray` | The features in time order, of shape (n_timesteps, n_features). | *required* |
| `y`  | `ndarray` | The targets, of shape (n_timesteps,) or (n_timesteps, 1).       | *required* |

Returns:

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

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

Load a fitted time-series wrapper from a `.eigenkit` bundle.

Parameters:

| Name       | Type           | Description                                                                                                                                | Default                                                                                |
| ---------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------- |
| `path`     | \`str          | Path\`                                                                                                                                     | The path of the .eigenkit bundle.                                                      |
| `provider` | \`ProviderSpec | None\`                                                                                                                                     | An optional provider that replaces the saved one. It is passed to the inner estimator. |
| `model`    | `Any`          | The nn.Module architecture, required when the inner estimator is GenericRegressor. Pass the same architecture as the one used in training. | `None`                                                                                 |

Returns:

| Type                           | Description         |
| ------------------------------ | ------------------- |
| `'QuantumTimeSeriesRegressor'` | The loaded wrapper. |

Raises:

| Type                 | Description                                             |
| -------------------- | ------------------------------------------------------- |
| `TypeError`          | If the bundle holds a different class.                  |
| `SerializationError` | If the inner estimator needs model and it is not given. |

#### `predict(X, y_history=None)`

Predict the regression targets of the windows of `X`.

Parameters:

| Name        | Type      | Description                                                     | Default                                                                                                                                     |
| ----------- | --------- | --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `X`         | `ndarray` | The features in time order, of shape (n_timesteps, n_features). | *required*                                                                                                                                  |
| `y_history` | \`ndarray | None\`                                                          | The past targets, aligned with the rows of X. They fill the autoregressive channel of each window. It is required when autoregressive=True. |

Returns:

| Type      | Description                                                     |
| --------- | --------------------------------------------------------------- |
| `ndarray` | The predictions, of shape (n_timesteps - in_size - shift + 1,). |

#### `score(X, y, *, return_dict=False)`

```python
score(
    X: np.ndarray,
    y: np.ndarray,
    *,
    return_dict: Literal[False] = False,
) -> float
```

```python
score(
    X: np.ndarray,
    y: np.ndarray,
    *,
    return_dict: Literal[True],
) -> dict[str, float]
```

Score the estimator on the window targets, aligned as in `predict`.

By default, it returns `R²`, for compatibility with scikit-learn (`cross_val_score`, `GridSearchCV`). Pass `return_dict=True` to get a dict with `r2`, `mse`, `mae`, `rmse` and `mape`.

Parameters:

| Name          | Type      | Description                                                                                    | Default    |
| ------------- | --------- | ---------------------------------------------------------------------------------------------- | ---------- |
| `X`           | `ndarray` | The samples, of shape (n_samples, n_features). The windows are built inside.                   | *required* |
| `y`           | `ndarray` | One target for each sample, of shape (n_samples,).                                             | *required* |
| `return_dict` | `bool`    | If True, every metric is returned in a dict, instead of the single main score. Default: False. | `False`    |

Returns:

| Type    | Description        |
| ------- | ------------------ |
| \`float | dict[str, float]\` |

### `RecurrentHQNC`

Bases: `ModelAccessMixin`, `BaseQuantumClassifier`

Hybrid classifier: a recurrent encoder, a quantum layer and a linear class head.

The recurrent encoder is a recurrent neural network (RNN). The estimator expects 3-D inputs of shape `(n_samples, seq_len, seq_feature_dim)`. By default, only the last timestep of the RNN output goes to the quantum layer. Set `rnn_return_last_timestep=False`, together with `sequence_length`, to use the full RNN output (flattened) instead. The quantum part is the same as in HQNC: `MinMaxNorm`, the quantum layer and an `nn.Linear` head.

Parameters:

| Name                       | Type                            | Description                                                                                                            | Default                                                                                                                                                                                                                                            |
| -------------------------- | ------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `seq_feature_dim`          | `int`                           | The number of features at each time step (the last axis of X).                                                         | *required*                                                                                                                                                                                                                                         |
| `rnn_type`                 | `Literal['rnn', 'gru', 'lstm']` | The type of recurrent layer: "rnn", "gru" (gated recurrent unit) or "lstm" (long short-term memory).                   | `'gru'`                                                                                                                                                                                                                                            |
| `rnn_hidden_dim`           | `int`                           | The hidden size in each direction.                                                                                     | `32`                                                                                                                                                                                                                                               |
| `rnn_num_layers`           | `int`                           | The number of stacked RNN layers.                                                                                      | `1`                                                                                                                                                                                                                                                |
| `rnn_dropout`              | `float`                         | The dropout between the RNN layers. It applies only if rnn_num_layers > 1.                                             | `0.0`                                                                                                                                                                                                                                              |
| `rnn_bidirectional`        | `bool`                          | If True, the RNN is bidirectional, and its output size doubles.                                                        | `False`                                                                                                                                                                                                                                            |
| `rnn_return_last_timestep` | `bool`                          | If True, only the last output step is kept. Otherwise, the full sequence is flattened, which requires sequence_length. | `True`                                                                                                                                                                                                                                             |
| `sequence_length`          | \`int                           | None\`                                                                                                                 | The number of time steps in each sample. It is required when rnn_return_last_timestep=False, and it must equal X.shape[1] in fit.                                                                                                                  |
| `in_quantum`               | \`int                           | None\`                                                                                                                 | The number of inputs of the quantum layer. It defaults to the size of the RNN output that reaches the quantum layer: rnn_hidden_dim, doubled when the RNN is bidirectional, and multiplied by sequence_length when rnn_return_last_timestep=False. |
| `n_qubits`                 | \`int                           | None\`                                                                                                                 | The number of qubits in each circuit. It defaults to in_quantum.                                                                                                                                                                                   |
| `depth`                    | `int`                           | The number of repetitions of the variational layer.                                                                    | `2`                                                                                                                                                                                                                                                |
| `n_classes`                | \`int                           | None\`                                                                                                                 | The number of output classes. If None, it is inferred from the labels passed to fit(). If it is given, it must equal the number of distinct labels in y.                                                                                           |
| `layer_type`               | `str`                           | The quantum layer. It is one of "Basic", "Reupload" and "Ensemble".                                                    | `'Basic'`                                                                                                                                                                                                                                          |
| `enc_rotation`             | \`Literal['RX', 'RY', 'RZ']     | None\`                                                                                                                 | The encoding rotation: "RX", "RY", "RZ" or None.                                                                                                                                                                                                   |
| `var_rotation`             | \`Literal['RX', 'RY', 'RZ']     | None\`                                                                                                                 | The variational rotation of the basic ansatz: "RX", "RY", "RZ" or None.                                                                                                                                                                            |
| `variational_type`         | \`Literal['basic', 'strong']    | None\`                                                                                                                 | The type of variational block: "basic", "strong" or None.                                                                                                                                                                                          |
| `basis`                    | \`Literal['X', 'Y', 'Z']        | None\`                                                                                                                 | The measurement basis: "X", "Y", "Z" or None. For these four arguments, None means "use the default of the layer named by layer_type", which is where each of these values is decided.                                                             |
| `provider`                 | `ProviderSpec`                  | The backend. It takes the same options as Circuit(provider=...).                                                       | `'numpy'`                                                                                                                                                                                                                                          |
| `lr`                       | `float`                         | The learning rate of the optimiser.                                                                                    | `0.01`                                                                                                                                                                                                                                             |
| `optimizer`                | `str`                           | The name of the optimiser. The supported names are "adam", "adamw", "rmsprop" and "sgd".                               | `'adam'`                                                                                                                                                                                                                                           |
| `optimizer_kwargs`         | \`dict                          | None\`                                                                                                                 | Extra keyword arguments passed to the optimiser.                                                                                                                                                                                                   |
| `epochs`                   | `int`                           | The number of training epochs.                                                                                         | `50`                                                                                                                                                                                                                                               |
| `batch_size`               | `int`                           | The size of each mini-batch.                                                                                           | `32`                                                                                                                                                                                                                                               |
| `show_progress`            | `bool`                          | If True, a tqdm progress bar is shown during training.                                                                 | `True`                                                                                                                                                                                                                                             |
| `random_state`             | \`int                           | None\`                                                                                                                 | The seed for the initialisation of the weights.                                                                                                                                                                                                    |

Attributes:

| Name                 | Type | Description                                                                                  |
| -------------------- | ---- | -------------------------------------------------------------------------------------------- |
| `train_losses_`      |      | The cross-entropy loss of each epoch, set by fit().                                          |
| `validation_losses_` |      | The validation cross-entropy loss of each epoch, set by fit() when validation data is given. |

#### `fit(X, y, X_val=None, y_val=None)`

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

Parameters:

| Name    | Type      | Description                                                    | Default                                                               |
| ------- | --------- | -------------------------------------------------------------- | --------------------------------------------------------------------- |
| `X`     | `ndarray` | The sequences, of shape (n_samples, seq_len, seq_feature_dim). | *required*                                                            |
| `y`     | `ndarray` | One label for each sequence, of shape (n_samples,).            | *required*                                                            |
| `X_val` | \`ndarray | None\`                                                         | Optional validation sequences, of the same shape as X. Default: None. |
| `y_val` | \`ndarray | None\`                                                         | The labels of X_val. Default: None.                                   |

Returns:

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

#### `predict(X)`

Predict a label for every sample in `X`.

Parameters:

| Name | Type      | Description                                                    | Default    |
| ---- | --------- | -------------------------------------------------------------- | ---------- |
| `X`  | `ndarray` | The sequences, of shape (n_samples, seq_len, seq_feature_dim). | *required* |

Returns:

| Type      | Description                                              |
| --------- | -------------------------------------------------------- |
| `ndarray` | One prediction for each sequence, of shape (n_samples,). |

#### `predict_proba(X)`

Predict the class probabilities of every sample in `X`.

Parameters:

| Name | Type      | Description                                                    | Default    |
| ---- | --------- | -------------------------------------------------------------- | ---------- |
| `X`  | `ndarray` | The sequences, of shape (n_samples, seq_len, seq_feature_dim). | *required* |

Returns:

| Type      | Description                                                                                     |
| --------- | ----------------------------------------------------------------------------------------------- |
| `ndarray` | The probabilities, of shape (n_samples, n_classes), with the columns in the order of classes\_. |

### `RecurrentHQNR`

Bases: `ModelAccessMixin`, `BaseQuantumRegressor`

Hybrid regressor: a recurrent encoder, a quantum layer and a linear head.

The recurrent encoder is a recurrent neural network (RNN). The estimator expects 3-D inputs of shape `(n_samples, seq_len, seq_feature_dim)`. By default, only the last timestep of the RNN output goes to the quantum layer. Set `rnn_return_last_timestep=False`, together with `sequence_length`, to use the full RNN output (flattened) instead. The quantum part is the same as in HQNR: `MinMaxNorm`, the quantum layer and an `nn.Linear` head.

Parameters:

| Name                       | Type                            | Description                                                                                                            | Default                                                                                                                                                                                                                                            |
| -------------------------- | ------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `seq_feature_dim`          | `int`                           | The number of features at each time step (the last axis of X).                                                         | *required*                                                                                                                                                                                                                                         |
| `rnn_type`                 | `Literal['rnn', 'gru', 'lstm']` | The type of recurrent layer: "rnn", "gru" (gated recurrent unit) or "lstm" (long short-term memory).                   | `'gru'`                                                                                                                                                                                                                                            |
| `rnn_hidden_dim`           | `int`                           | The hidden size in each direction.                                                                                     | `32`                                                                                                                                                                                                                                               |
| `rnn_num_layers`           | `int`                           | The number of stacked RNN layers.                                                                                      | `1`                                                                                                                                                                                                                                                |
| `rnn_dropout`              | `float`                         | The dropout between the RNN layers. It applies only if rnn_num_layers > 1.                                             | `0.0`                                                                                                                                                                                                                                              |
| `rnn_bidirectional`        | `bool`                          | If True, the RNN is bidirectional, and its output size doubles.                                                        | `False`                                                                                                                                                                                                                                            |
| `rnn_return_last_timestep` | `bool`                          | If True, only the last output step is kept. Otherwise, the full sequence is flattened, which requires sequence_length. | `True`                                                                                                                                                                                                                                             |
| `sequence_length`          | \`int                           | None\`                                                                                                                 | The number of time steps in each sample. It is required when rnn_return_last_timestep=False, and it must equal X.shape[1] in fit.                                                                                                                  |
| `in_quantum`               | \`int                           | None\`                                                                                                                 | The number of inputs of the quantum layer. It defaults to the size of the RNN output that reaches the quantum layer: rnn_hidden_dim, doubled when the RNN is bidirectional, and multiplied by sequence_length when rnn_return_last_timestep=False. |
| `n_qubits`                 | \`int                           | None\`                                                                                                                 | The number of qubits in each circuit. It defaults to in_quantum.                                                                                                                                                                                   |
| `depth`                    | `int`                           | The number of repetitions of the variational layer.                                                                    | `2`                                                                                                                                                                                                                                                |
| `layer_type`               | `str`                           | The quantum layer. It is one of "Basic", "Reupload" and "Ensemble".                                                    | `'Basic'`                                                                                                                                                                                                                                          |
| `enc_rotation`             | \`Literal['RX', 'RY', 'RZ']     | None\`                                                                                                                 | The encoding rotation: "RX", "RY", "RZ" or None.                                                                                                                                                                                                   |
| `var_rotation`             | \`Literal['RX', 'RY', 'RZ']     | None\`                                                                                                                 | The variational rotation of the "basic" ansatz: "RX", "RY", "RZ" or None.                                                                                                                                                                          |
| `variational_type`         | \`Literal['basic', 'strong']    | None\`                                                                                                                 | The type of variational block: "basic", "strong" or None.                                                                                                                                                                                          |
| `basis`                    | \`Literal['X', 'Y', 'Z']        | None\`                                                                                                                 | The measurement basis: "X", "Y", "Z" or None. For these four arguments, None means "use the default of the layer named by layer_type", which is where each of these values is decided.                                                             |
| `provider`                 | `ProviderSpec`                  | The backend. It takes the same options as Circuit(provider=...).                                                       | `'numpy'`                                                                                                                                                                                                                                          |
| `lr`                       | `float`                         | The learning rate of the optimiser.                                                                                    | `0.01`                                                                                                                                                                                                                                             |
| `optimizer`                | `str`                           | The name of the optimiser.                                                                                             | `'adam'`                                                                                                                                                                                                                                           |
| `optimizer_kwargs`         | \`dict                          | None\`                                                                                                                 | Extra keyword arguments passed to the optimiser.                                                                                                                                                                                                   |
| `epochs`                   | `int`                           | The number of training epochs.                                                                                         | `50`                                                                                                                                                                                                                                               |
| `batch_size`               | `int`                           | The size of each mini-batch.                                                                                           | `32`                                                                                                                                                                                                                                               |
| `show_progress`            | `bool`                          | If True, a tqdm progress bar is shown.                                                                                 | `True`                                                                                                                                                                                                                                             |
| `loss_func`                | `str`                           | The regression loss: "mse", "mae" or "huber".                                                                          | `'mse'`                                                                                                                                                                                                                                            |
| `huber_delta`              | `float`                         | The threshold of the Huber loss.                                                                                       | `1.0`                                                                                                                                                                                                                                              |
| `huber_reduction`          | `str`                           | The reduction of the Huber loss: "mean" or "sum".                                                                      | `'mean'`                                                                                                                                                                                                                                           |
| `random_state`             | \`int                           | None\`                                                                                                                 | The seed.                                                                                                                                                                                                                                          |

Attributes:

| Name                 | Type | Description                                                                    |
| -------------------- | ---- | ------------------------------------------------------------------------------ |
| `train_losses_`      |      | The training loss of each epoch, set by fit().                                 |
| `validation_losses_` |      | The validation loss of each epoch, set by fit() when validation data is given. |

#### `fit(X, y, X_val=None, y_val=None)`

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

Parameters:

| Name    | Type      | Description                                                            | Default                                                               |
| ------- | --------- | ---------------------------------------------------------------------- | --------------------------------------------------------------------- |
| `X`     | `ndarray` | The sequences, of shape (n_samples, seq_len, seq_feature_dim).         | *required*                                                            |
| `y`     | `ndarray` | One target for each sequence, of shape (n_samples,) or (n_samples, 1). | *required*                                                            |
| `X_val` | \`ndarray | None\`                                                                 | Optional validation sequences, of the same shape as X. Default: None. |
| `y_val` | \`ndarray | None\`                                                                 | The targets of X_val. Default: None.                                  |

Returns:

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

#### `predict(X)`

Predict a value for every sample in `X`.

Parameters:

| Name | Type      | Description                                                    | Default    |
| ---- | --------- | -------------------------------------------------------------- | ---------- |
| `X`  | `ndarray` | The sequences, of shape (n_samples, seq_len, seq_feature_dim). | *required* |

Returns:

| Type      | Description                                                                 |
| --------- | --------------------------------------------------------------------------- |
| `ndarray` | One prediction for each sequence, as a float32 array of shape (n_samples,). |

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