## `eigenkit.torch.estimators.generic`

Estimators that wrap any `nn.Module`.

They wrap any `torch.nn.Module` that you supply in a `fit` and `predict` interface compatible with scikit-learn, with the standard training and inference loops of the library. You must build the complete model, including any output head.

## `eigenkit.torch.estimators.generic.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. |

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

## `eigenkit.torch.estimators.generic.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\_. |

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