Source code for autoemulate.emulators.polynomials

import torch
from torch import nn
from torch.optim.lr_scheduler import LRScheduler

from autoemulate.core.device import TorchDeviceMixin
from autoemulate.core.types import DeviceLike, TensorLike
from autoemulate.data.utils import set_random_seed
from autoemulate.emulators.base import PyTorchBackend
from autoemulate.feature_generation.polynomial_features import PolynomialFeatures
from autoemulate.transforms.standardize import StandardizeTransform


[docs] class PolynomialRegression(PyTorchBackend): """ PolynomialRegression emulator. Implements a linear model including all main effects, interactions, and quadratic terms. """ def __init__( self, x: TensorLike, y: TensorLike, standardize_x: bool = False, standardize_y: bool = False, degree: int = 2, lr: float = 0.1, epochs: int = 500, batch_size: int = 16, random_seed: int | None = None, device: DeviceLike | None = None, scheduler_cls: type[LRScheduler] | None = None, scheduler_params: dict | None = None, ): """Initialize a PolynomialRegression emulator. Parameters ---------- x: TensorLike Input features. y: TensorLike Target values. standardize_x: bool Whether to standardize input features. Defaults to False. standardize_y: bool Whether to standardize target values. Defaults to False. degree: int Degree of the polynomial features to be generated. Defaults to 2. lr: float Learning rate for the optimizer. Defaults to 0.1. epochs: int Number of training epochs. Defaults to 500. batch_size: int Batch size for training. Defaults to 16. random_seed: int | None Random seed for reproducibility. Defaults to None. device: DeviceLike | None Device to run the model on. If None, uses the default device. Defaults to None. scheduler_cls: type[LRScheduler] | None Learning rate scheduler class. If None, no scheduler is used. Defaults to None. scheduler_params: dict | None Additional keyword arguments related to the scheduler. """ super().__init__() TorchDeviceMixin.__init__(self, device=device) if random_seed is not None: set_random_seed(seed=random_seed) self.x_transform = StandardizeTransform() if standardize_x else None self.y_transform = StandardizeTransform() if standardize_y else None self.degree = degree self.lr = lr self.epochs = epochs self.batch_size = batch_size self.n_features = x.shape[1] self.n_outputs = y.shape[1] if y.ndim > 1 else 1 # includes bias term by default self.poly = PolynomialFeatures( self.n_features, degree=self.degree, device=self.device ) self.linear = nn.Linear( self.poly.n_output_features, self.n_outputs, bias=False ).to(self.device) self.optimizer = self.optimizer_cls(self.linear.parameters(), lr=self.lr) # type: ignore[call-arg] since all optimizers include lr self.scheduler_cls = scheduler_cls self.scheduler_params = scheduler_params or {} self.scheduler_setup(self.scheduler_params)
[docs] def forward(self, x: torch.Tensor) -> torch.Tensor: """Forward pass through for polynomial regression.""" # Transform input using the fitted PolynomialFeatures x_poly = self.poly(x) return self.linear(x_poly)
[docs] @staticmethod def is_multioutput() -> bool: """Polynomial regression supports multi-output.""" return True
[docs] @staticmethod def get_tune_params(): """Return a dictionary of hyperparameters to tune.""" scheduler_specs = PolynomialRegression.get_scheduler_params() return { "lr": [1e-3, 1e-2, 1e-1, 2e-1], "epochs": [50, 100, 200, 500, 1000], "batch_size": [8, 16, 32], "scheduler_cls": scheduler_specs["scheduler_cls"], "scheduler_params": scheduler_specs["scheduler_params"], }