Configuration System#

Source Files
  • twiga/core/config/base.py

Twiga follows a configuration-as-code pattern: every component - data preprocessing, model architecture, training orchestration, and uncertainty quantification - is controlled by a Pydantic model that validates inputs at construction time. This page is the complete field reference for every configuration class in the framework.

Class Hierarchy#

The diagram below shows how the configuration classes relate to one another. BaseModelConfig is the root for all model configs; NeuralModelConfig extends it with PyTorch Lightning training parameters. DataPipelineConfig, ForecasterConfig, and ConformalConfig are standalone configuration objects that the TwigaForecaster accepts at construction time.

        classDiagram
    class BaseSearchSpace {
        +model_config: ConfigDict
        +validate_search_space() BaseSearchSpace
        +get_optuna_params(trial, prefix) dict
        +_should_use_log(low, high) bool
    }

    class BaseModelConfig {
        +name: Literal["base_model"]
        +domain: Literal["nn"]
        +search_space: BaseSearchSpace | None
        +get_optuna_params(trial) dict
    }

    class NeuralModelConfig {
        +name: Literal["neural_model"]
        +rich_progress_bar: bool
        +wandb_logging: bool
        +batch_size: int
        +max_epochs: int
        +patience: int
        +seed: int
        +from_data_config(data_config) NeuralModelConfig
    }

    class DataPipelineConfig {
        +target_feature: list | str
        +period: str
        +lookback_window_size: int
        +forecast_horizon: int
        +calendar_features: list | None
        +lags: list | None
    }

    class ForecasterConfig {
        +split_freq: Literal
        +train_size: int
        +test_size: int
        +window: Literal
        +project_name: str
        +seed: int
    }

    class ConformalConfig {
        +method: Literal
        +score_type: Literal
        +alpha: float
    }

    BaseModelConfig --> BaseSearchSpace : search_space
    NeuralModelConfig --|> BaseModelConfig : extends
    

How Configs Flow Through the System#

Each configuration object targets a specific subsystem of the TwigaForecaster. The diagram below illustrates which config drives which component.

        flowchart LR
    DC[DataPipelineConfig] --> DP[DataPipeline]
    FC[ForecasterConfig] --> TF[TwigaForecaster]
    MC[Model Configs] --> M[Models]
    CC[ConformalConfig] --> CP[Conformal Predictor]

    TF --> DP
    TF --> M
    TF --> CP
    TF --> BT[Backtester / TimeBasedCV]

    MC -. "ML configs" .-> ML[CatBoost / XGBoost / LightGBM / Linear]
    MC -. "NN configs" .-> NN[MLPF / MLPGAM / MLPGAF / NHITS / GANF]
    
  • ForecasterConfig controls the TwigaForecaster itself - project naming, cross-validation splits, and output directories.

  • DataPipelineConfig controls the DataPipeline - feature engineering, scaling, and sequence creation.

  • Model configs (BaseModelConfig subclasses for ML models, NeuralModelConfig subclasses for NN models) define model architecture and training behavior.

  • ConformalConfig controls conformal prediction - the method, score type, and significance level used for prediction intervals.

DataPipelineConfig#

DataPipelineConfig defines how raw time series data is transformed into model-ready features. It is passed as data_params to TwigaForecaster and drives the DataPipeline.

Field

Type

Default

Description

target_feature

list[str] | str

required

Target variable name(s) to forecast. A single string for univariate forecasting, or a list for multivariate.

period

str

required

Sampling frequency of the time series, expressed as a pandas offset alias (e.g., "1h", "30min", "1D").

lookback_window_size

int

required

Number of past time steps used as model input.

forecast_horizon

int

required

Number of future time steps the model predicts.

latitude

float | None

None

Latitude for computing day/night features via solar position. Required when using "day_night" in calendar_features.

longitude

float | None

None

Longitude for computing day/night features via solar position.

historical_features

list[str] | None

None

Column names for features with unknown future values (available only during the lookback window).

calendar_features

list[str] | None

None

Cyclical temporal features derived from the timestamp column (e.g., "hour", "dayofweek", "month", "day_night").

exogenous_features

list[str] | None

None

Known future features available over the full lookback + forecast horizon (e.g., weather forecasts).

future_covariates

list[str] | None

None

Known future features available over the forecast horizon only.

input_scaler

object | None

None

A scikit-learn-compatible scaler applied to input features (e.g., StandardScaler(), RobustScaler()).

target_scaler

object | None

None

A scikit-learn-compatible scaler applied to the target variable.

lags

list[int] | None

None

Lag intervals (in periods) for autoregressive feature engineering (e.g., [1, 24, 48, 168]).

windows

list[int] | int | None

None

Window sizes for rolling statistics (e.g., [24, 48] or 24).

window_funcs

list[str] | str | None

None

Aggregation functions applied to rolling windows (e.g., ["mean", "std"] or "mean").

date_column

str

"timestamp"

Name of the datetime column in the input DataFrame.

stride

int

1

Step between consecutive sliding windows. Default 1 produces fully overlapping windows (maximum data augmentation). Set to forecast_horizon for non-overlapping windows — recommended for baseline evaluation to avoid pseudo-replication.

Example#

from sklearn.preprocessing import RobustScaler, StandardScaler
from twiga.core.config import DataPipelineConfig

data_config = DataPipelineConfig(
    target_feature="load_mw",
    period="1h",
    lookback_window_size=168,      # 7 days of hourly data
    forecast_horizon=48,           # predict 2 days ahead
    calendar_features=["hour", "dayofweek", "month"],
    exogenous_features=["ghi", "temperature"],
    latitude=-6.8,
    longitude=39.3,
    lags=[1, 24, 48, 168],
    windows=[24, 48],
    window_funcs=["mean", "std"],
    input_scaler=StandardScaler(),
    target_scaler=RobustScaler(),
)

Choosing lags and windows

Pick lags that correspond to meaningful seasonal periods in your data. For hourly electricity data, [1, 24, 48, 168] captures the previous hour, same hour yesterday, two days ago, and one week ago. Rolling windows (windows + window_funcs) smooth out noise and expose trends.

API Reference#

class twiga.core.config.DataPipelineConfig(**data)#

Bases: BaseModel

Configuration for a time-series data pipeline.

Captures everything the pipeline needs to know about the raw dataset: which column to forecast, which features are available, how long the lookback and forecast windows are, what scalers to apply, and which lag/rolling-window features to engineer.

Parameters:
  • target_feature (list[str] | str) – Target variable name(s) to forecast.

  • period (str) – Sampling frequency using pandas offset aliases (e.g. "1H", "30min").

  • lookback_window_size (int) – Number of past timesteps fed to the model as input.

  • forecast_horizon (int) – Number of future timesteps to predict.

  • latitude (float | None, optional) – Latitude for day/night feature calculation. Defaults to None.

  • longitude (float | None, optional) – Longitude for day/night feature calculation. Defaults to None.

  • historical_features (list[str] | None, optional) – Features whose future values are unknown (historical context only). Defaults to None.

  • calendar_features (list[str] | None, optional) – Cyclical temporal features derived from the timestamp column. Defaults to None.

  • exogenous_features (list[str] | None, optional) – Features known over the full lookback + forecast horizon. Defaults to None.

  • future_covariates (list[str] | None, optional) – Features known only over the forecast horizon. Defaults to None.

  • input_scaler (object | None, optional) – Scaler applied to input features. Defaults to None.

  • target_scaler (object | None, optional) – Scaler applied to the target variable. Defaults to None.

  • lags (list[int] | None, optional) – Lag intervals in periods for feature engineering. Defaults to None.

  • windows (list[int] | int | None, optional) – Window sizes for rolling statistics. Defaults to None.

  • window_funcs (list[str] | str | None, optional) – Aggregation functions applied to rolling windows (e.g. "mean", "std"). Defaults to None.

  • date_column (str, optional) – Name of the datetime column. Defaults to "timestamp".

calendar_features: list[str] | None#
date_column: str#
exogenous_features: list[str] | None#
forecast_horizon: int#
future_covariates: list[str] | None#
historical_features: list[str] | None#
input_scaler: object | None#
lags: list[int] | None#
latitude: float | None#
longitude: float | None#
lookback_window_size: int#
model_config: ClassVar[ConfigDict] = {}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

period: str#
stride: int#
target_feature: list[str] | str#
target_scaler: object | None#
window_funcs: list[str] | str | None#
windows: list[int] | int | None#

ForecasterConfig#

ForecasterConfig controls the training orchestration layer: how the data is split for backtesting, where outputs are saved, and which evaluation metrics to compute. It is passed as train_params to TwigaForecaster.

Field

Type

Default

Description

domain

Literal["ml"]

"ml"

Internal domain identifier. Fixed to "ml" and excluded from parameter dumps.

split_freq

Literal["days", "minutes", "hours", "weeks", "months", "years"]

"months"

Time unit for train_size, test_size, gap, and stride.

test_size

int

1

Length of each test fold in units of split_freq.

train_size

int

1

Length of each training fold in units of split_freq (used with "rolling" window).

gap

int

0

Number of split_freq units between the end of training data and the start of test data. Useful when your model should not see the most recent observations.

stride

int | None

None

Step size (in split_freq units) between successive folds. Defaults to test_size when None.

window

Literal["expanding", "rolling"]

"expanding"

Cross-validation window strategy. "expanding" grows the training set with each fold; "rolling" keeps a fixed-size window.

num_splits

int | None

None

Maximum number of cross-validation splits. None uses all available splits.

project_name

str

"experiment"

Project name used for organizing output directories and experiment tracking.

file_name

str | None

None

Optional file name prefix for saved artifacts.

seed

int

42

Random seed for reproducibility.

date_column

str

"timestamp"

Name of the datetime column in the input DataFrame.

root_dir

str

"../"

Root directory for saving model checkpoints and results.

metrics

tuple[str] | list[str] | None

None

Evaluation metrics to compute (e.g., ["mae", "rmse", "smape"]). When None, all default metrics are used.

Example#

from twiga.core.config import ForecasterConfig

train_config = ForecasterConfig(
    split_freq="days",
    train_size=14,
    test_size=7,
    gap=0,
    window="expanding",
    project_name="MyProject",
    root_dir="./outputs",
    seed=42,
    metrics=["mae", "rmse", "smape"],
)

Expanding vs. rolling windows

With window="expanding", each successive fold includes all prior training data - the training set grows over time. With window="rolling", the training window is fixed at train_size units and slides forward. Expanding windows generally produce more stable models; rolling windows are better when the data distribution shifts significantly over time. See Backtesting for a detailed comparison.

API Reference#

class twiga.core.config.ForecasterConfig(**data)#

Bases: BaseModel

Configuration for the forecaster cross-validation runner.

Controls how the time-series is split for evaluation (split frequency, window type, train/test sizes), and holds project-level metadata such as the project name and output file name.

Parameters:
  • domain (Literal["ml"], optional) – Modelling domain identifier. Fixed to "ml"; excluded from parameter tuning. Defaults to "ml".

  • split_freq (str, optional) – Unit for train_size, test_size, and gap. One of "days", "hours", "weeks", "months", "years". Defaults to "months".

  • test_size (int, optional) – Number of split_freq units in each test fold. Defaults to 1.

  • train_size (int, optional) – Number of split_freq units in each training fold (rolling window only). Defaults to 1.

  • gap (int, optional) – Number of split_freq units between the end of the training fold and the start of the test fold. Defaults to 0.

  • stride (int | None, optional) – Step size between consecutive splits in split_freq units. None uses test_size as the stride. Defaults to None.

  • window (Literal["expanding", "rolling"], optional) – Cross-validation window strategy. Defaults to "expanding".

  • num_splits (int | None, optional) – Maximum number of CV splits. None uses all available splits. Defaults to None.

  • project_name (str, optional) – Experiment / project name used for logging and output paths. Defaults to "experiment".

  • file_name (str | None, optional) – Output file name. None auto-generates from the project name. Defaults to None.

  • seed (int, optional) – Random seed for reproducibility. Defaults to 42.

  • date_column (str, optional) – Name of the datetime column in the dataset. Defaults to "timestamp".

  • root_dir (str, optional) – Root directory for output artefacts. Defaults to "../".

  • metrics (tuple[str] | list[str] | None, optional) – Evaluation metrics to compute and log. None uses the runner’s defaults. Defaults to None.

checkpoints_path: str | None#
date_column: str#
domain: Literal['ml']#
file_name: str | None#
gap: int#
metrics: tuple[str, ...] | list[str] | None#
model_config: ClassVar[ConfigDict] = {}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

num_splits: int | None#
project_name: str#
root_dir: str#
seed: int#
split_freq: Literal['days', 'minutes', 'hours', 'weeks', 'months', 'years']#
stride: int | None#
test_size: int#
train_size: int#
window: Literal['expanding', 'rolling']#

BaseModelConfig#

BaseModelConfig is the root configuration class for all model types. It provides the name and domain identifiers, an optional search_space for hyperparameter tuning, and the get_optuna_params() method that merges fixed parameters with Optuna trial suggestions.

You do not instantiate BaseModelConfig directly. Instead, each model provides its own subclass (e.g., XGBOOSTConfig, MLPFConfig) that inherits from either BaseModelConfig (for ML models) or NeuralModelConfig (for NN models).

Field

Type

Default

Description

name

Literal["base_model"]

"base_model"

Model type identifier. Overridden by each subclass. Excluded from parameter dumps.

domain

Literal["nn"]

"nn"

Modeling domain identifier (aliased as model_type). Excluded from parameter dumps.

search_space

BaseSearchSpace | None

None

Optional hyperparameter search space for Optuna-based tuning. Excluded from parameter dumps.

Key Method: get_optuna_params(trial)#

Combines the model’s fixed parameters (everything except name and search_space) with dynamic suggestions from the search space. During hyperparameter tuning, the TwigaForecaster calls this method inside the Optuna objective function.

def get_optuna_params(self, trial: optuna.Trial) -> dict[str, Any]:
    base_params = self.model_dump(exclude={"name", "search_space"})
    if self.search_space:
        search_params = self.search_space.get_optuna_params(trial, prefix=self.name)
        return {**base_params, **search_params}
    return base_params

API Reference#

class twiga.core.config.BaseModelConfig(**data)#

Bases: BaseModel

Shared base configuration for all forecasting models.

Provides the name, domain, and search_space fields that every concrete config is expected to expose, along with a uniform get_optuna_params() that merges fixed config values with any search-space suggestions.

Subclass this to define model-specific configurations:

class MyModelConfig(BaseModelConfig):
    name: Literal["my_model"] = Field(default="my_model", exclude=True)
    hidden_size: int = 128
    dropout: float = 0.3
    search_space: BaseSearchSpace = BaseSearchSpace(
        hidden_size=[64, 128, 256],
        dropout=(0.0, 0.5),
    )
Parameters:
  • name (Literal["base_model"], optional) – Model type identifier. Excluded from parameter tuning. Defaults to "base_model".

  • domain (Literal["nn"], optional) – Modelling domain identifier. Excluded from parameter tuning. Defaults to "nn".

  • search_space (BaseSearchSpace | None, optional) – Hyperparameter search space. When set, its fields are merged into the output of get_optuna_params() for HPO. Defaults to None.

domain: Literal['nn']#
get_optuna_params(trial)#

Return fixed config values merged with Optuna search-space suggestions.

Fixed parameters come from pydantic.BaseModel.model_dump() (with name and search_space excluded). If a search_space is set, its fields are sampled for trial and override any overlapping fixed values, allowing a single config object to serve both fixed and tuned usage patterns.

Parameters:

trial (Trial) – Active Optuna trial.

Return type:

dict[str, Any]

Returns:

dict[str, Any]

Combined parameter dict ready to pass to the model

constructor.

model_config: ClassVar[ConfigDict] = {'extra': 'allow'}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

name: Literal['base_model']#
search_space: BaseSearchSpace | None#

NeuralModelConfig#

NeuralModelConfig extends BaseModelConfig with parameters specific to PyTorch Lightning training. All neural network models (MLPF, MLPGAM, MLPGAF, NHITS, GANF, and their probabilistic variants) inherit from this class.

Field

Type

Default

Description

name

Literal["neural_model"]

"neural_model"

Model type identifier. Overridden by each NN subclass. Excluded from parameter dumps.

domain

Literal["nn"]

"nn"

Modeling domain identifier. Excluded from parameter dumps.

rich_progress_bar

bool

True

Enable rich-formatted progress bars during training.

wandb_logging

bool

False

Enable experiment logging to Weights & Biases. Requires the tracking optional dependency group.

drop_last

bool

True

Drop the last incomplete batch if the dataset size is not divisible by the batch size. Prevents shape mismatches.

num_workers

int

8

Number of worker processes for the PyTorch DataLoader. Set to 0 on Windows if you encounter multiprocessing errors.

batch_size

int

64

Mini-batch size for training and validation.

pin_memory

bool

True

Pin host memory in the DataLoader for faster CPU-to-GPU transfer.

max_epochs

int

10

Maximum number of training epochs. Training may stop earlier if early stopping triggers.

metric

str

"mae"

Metric used to monitor model performance and trigger early stopping (e.g., "mae", "mse").

optimizer_params

dict | None

None

Optimizer configuration dictionary (e.g., {"adamw": {"params": {"lr": 1e-3, "weight_decay": 1e-6}}}).

scheduler_params

dict | None

None

Learning rate scheduler configuration dictionary (e.g., {"multi_step": {"params": {"gamma": 0.1, "prob_decay_1": 0.5}}}).

patience

int

10

Number of epochs with no improvement before early stopping halts training.

resume_training

bool

True

Resume training from the last saved checkpoint if one exists.

seed

int

42

Random seed for reproducibility. Must be a positive integer (gt=0).

Key Method: from_data_config(data_config, **kwargs)#

A classmethod that constructs a NeuralModelConfig (or any subclass) directly from a DataPipelineConfig. It automatically computes the input and output dimensions that the neural architecture needs:

from twiga.core.config import DataPipelineConfig
from twiga.models.nn.mlpf_model import MLPFConfig

data_config = DataPipelineConfig(
    target_feature="load_mw",
    period="1h",
    lookback_window_size=168,
    forecast_horizon=48,
    calendar_features=["hour", "dayofweek"],
    exogenous_features=["ghi"],
)

# Automatically sets num_target_feature, num_calendar_features, etc.
mlpf_config = MLPFConfig.from_data_config(data_config, max_epochs=50, batch_size=128)

The classmethod extracts the following from the DataPipelineConfig:

  • num_target_feature - count of target variables

  • num_historical_features - count of historical feature columns

  • num_calendar_features - count of calendar feature columns

  • num_exogenous_features - count of exogenous feature columns

  • num_future_covariates - count of future covariate columns

  • forecast_horizon and lookback_window_size - passed through directly

Any additional keyword arguments are forwarded to the config constructor, allowing you to override defaults like max_epochs or batch_size.

Always use from_data_config for NN models

Neural models require dimensional parameters (num_target_feature, num_calendar_features, etc.) that must match the data pipeline. Using from_data_config guarantees consistency. Setting these values manually is error-prone and not recommended.

API Reference#

class twiga.core.config.NeuralModelConfig(**data)#

Bases: BaseModelConfig

Configuration for neural network-based forecasting models.

Extends BaseModelConfig with training infrastructure fields and a shared three-dict HPO system for optimizer, scheduler, and batch-size search. See the module docstring for a full explanation of the search space design.

The optimizer and scheduler are selected via optimizer_type and lr_scheduler_type. Both are captured by save_hyperparameters() in BaseNeuralModel at training time, so they must be declared as fields here.

Optional fine-grained overrides can be supplied via optimizer_params and scheduler_params. When provided they are merged into the corresponding entry of BaseNeuralModel.OPTIMIZERS / BaseNeuralModel.SCHEDULERS, allowing partial overrides (e.g. only lr) without replacing the full dict.

Parameters:
  • name (Literal["neural_model"], optional) – Model type identifier. Defaults to "neural_model".

  • domain (Literal["nn"], optional) – Modelling domain identifier. Defaults to "nn".

  • rich_progress_bar (bool, optional) – Enable rich progress bars. Defaults to True.

  • wandb_logging (bool, optional) – Enable Weights & Biases logging. Defaults to False.

  • drop_last (bool, optional) – Drop the last incomplete batch. Defaults to True.

  • num_workers (int, optional) – DataLoader worker count. Defaults to 8.

  • batch_size (int, optional) – Training batch size. Defaults to 64.

  • pin_memory (bool, optional) – Pin memory for faster GPU transfer. Defaults to True.

  • max_epochs (int, optional) – Maximum training epochs. Defaults to 10.

  • patience (int, optional) – Early-stopping patience in epochs. Defaults to 10.

  • resume_training (bool, optional) – Resume from last checkpoint. Defaults to True.

  • seed (int, optional) – Positive integer random seed. Defaults to 42.

  • metric (Literal["mae", "mse", "smape"], optional) – Validation metric. Defaults to "mae".

  • optimizer_type (Literal[...], optional) – Native torch.optim optimizer. Defaults to "adamw".

  • lr_scheduler_type (Literal[...], optional) – Native torch.optim.lr_scheduler class. Defaults to "multi_step".

  • optimizer_params (dict | None, optional) – Partial override for the selected optimizer’s default params. Defaults to None.

  • scheduler_params (dict | None, optional) – Partial override for the selected scheduler’s default params. Defaults to None.

BASE_TRAINING_SEARCH_SPACE: ClassVar[BaseSearchSpace] = BaseSearchSpace(optimizer_type=['adam', 'adamw'], lr_scheduler_type=['warmup_cosine', 'multi_step', 'reduce_on_plateau'], batch_size=[8, 16, 32, 64])#
batch_size: int#
domain: Literal['nn']#
drop_last: bool#
classmethod from_data_config(data_config, **kwargs)#

Create a config instance with dimensions derived from a DataPipelineConfig.

Parameters:
  • data_config (DataPipelineConfig) – Pipeline config providing feature counts and sequence dimensions.

  • **kwargs – Additional fields forwarded to the constructor, allowing any field to be overridden at instantiation time.

Returns:

NeuralModelConfig – Populated config instance.

Raises:
  • TypeError – If data_config.target_feature is not str or list[str].

  • AttributeError – If data_config is missing forecast_horizon.

get_optuna_params(trial)#

Standard HPO sampling for all neural models.

Combines child-specific architecture parameters with the standardized conditional optimizer and scheduler search space.

Return type:

dict

gradient_clip_val: float | None#
lr_scheduler_type: Literal['step', 'multi_step', 'multiplicative', 'exponential', 'constant', 'linear_decay', 'polynomial', 'cosine_annealing', 'cosine_annealing_lr', 'cyclic', 'reduce_on_plateau', 'one_cycle', 'warmup_multi_step', 'warmup_cosine']#
max_epochs: int#
metric: Literal['mae', 'mse', 'smape']#
model_config: ClassVar[ConfigDict] = {'extra': 'allow'}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

monitor: Literal['loss', 'mae', 'mse', 'smape', 'sigma_loss'] | None#
name: Literal['neural_model']#
num_workers: int#
optimizer_params: dict | None#
optimizer_type: Literal['adam', 'adamw', 'nadam', 'radam', 'adamax', 'adafactor', 'adagrad', 'adadelta', 'rmsprop', 'rprop', 'asgd', 'sgd', 'muon']#
patience: int#
pin_memory: bool#
resume_training: bool#
rich_progress_bar: bool#
classmethod sample_training_params(trial)#

Sample optimizer, scheduler, and batch-size using BaseSearchSpace logic.

Return type:

dict

scheduler_params: dict | None#
seed: int#
wandb_logging: bool#

BaseSearchSpace#

BaseSearchSpace defines the hyperparameter search space for Optuna-based tuning. Each model config can optionally include a search_space field containing a subclass of BaseSearchSpace.

Search spaces use two conventions:

  • Tuples (low, high) define numeric ranges. Integer tuples map to trial.suggest_int(); float tuples map to trial.suggest_float().

  • Lists [a, b, c] define categorical choices and map to trial.suggest_categorical().

Feature

Encoding

Optuna Method

Details

Integer range

tuple[int, int]

suggest_int

e.g., (3, 12) for max_depth

Float range

tuple[float, float]

suggest_float

Log scale applied automatically when high / low >= 10 and both values are positive

Categorical

list[Any]

suggest_categorical

e.g., ["relu", "tanh", "gelu"]

Validation Rules#

The validate_search_space() model validator enforces the following at construction time:

  1. Tuples must have exactly two numeric elements with low < high.

  2. Lists must contain at least one element and have no duplicates.

  3. All fields must be either tuples or lists - no other types are allowed.

Automatic Log Scale#

The _should_use_log(low, high) static method determines whether Optuna should sample on a logarithmic scale. It returns True when both bounds are positive and their ratio is at least 10. This is useful for parameters like learning rate where the interesting range spans several orders of magnitude (e.g., (0.0001, 0.1)).

Example#

from twiga.core.config import BaseSearchSpace

class XGBoostSearchSpace(BaseSearchSpace):
    learning_rate: tuple[float, float] = (0.001, 0.3)   # log scale (ratio >= 10)
    max_depth: tuple[int, int] = (3, 12)                 # integer range
    n_estimators: tuple[int, int] = (100, 1000)           # integer range
    subsample: tuple[float, float] = (0.5, 1.0)          # linear scale (ratio < 10)
    reg_alpha: list[float] = [0.0, 0.1, 1.0, 10.0]      # categorical

# Attach to a model config
from twiga.models.ml.xgboost_model import XGBOOSTConfig

config = XGBOOSTConfig(
    search_space=XGBoostSearchSpace(),
)

API Reference#

class twiga.core.config.BaseSearchSpace(**data)#

Bases: BaseModel

Pydantic model for validating hyperparameter optimisation search spaces.

Each field must be either:

  • A tuple[float, float] or tuple[int, int] representing a continuous range (low, high). Float ranges spanning more than one order of magnitude (high / low >= 10) are sampled on a log scale automatically.

  • A list of at least one categorical value.

The class uses extra="allow" so that concrete search spaces can be defined inline without subclassing:

space = BaseSearchSpace(
    latent_size=[64, 128, 256],
    dropout=(0.0, 0.5),
)
Parameters:

**kwargs – Any keyword argument whose value is a valid range tuple or categorical list.

Examples

>>> space = BaseSearchSpace(lr=(1e-4, 1e-2), activation=["relu", "tanh"])
>>> params = space.get_optuna_params(trial, prefix="mlp")
get_optuna_params(trial, prefix='')#

Generate Optuna parameter suggestions for all fields.

Parameters:
  • trial (Trial) – Active Optuna trial.

  • prefix (str) – Prefix prepended to each parameter name in the trial (e.g. the model name) to avoid collisions when multiple search spaces are sampled in the same trial. Defaults to "".

Return type:

dict[str, Any]

Returns:

dict[str, Any]

Mapping of field names (without prefix) to their

sampled values.

model_config: ClassVar[ConfigDict] = {'extra': 'allow'}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

validate_against(config)#

Raise ValueError if any search space field name is not present on config.

Catches typos in search space definitions early - before an Optuna trial is run - so that mis-spelled field names produce a clear error instead of silently sampling a parameter that never gets applied.

Parameters:

config (BaseModel) – The model config instance (or class) whose fields define the valid parameter names.

Raises:

ValueError – If one or more field names in this search space do not exist on config.

Examples

Return type:

None

>>> space = BaseSearchSpace(hiddn_dim=[64, 128])  # typo!
>>> space.validate_against(my_model_config)
Traceback (most recent call last):
    ...
ValueError: Search space contains unknown fields: {'hiddn_dim'}. ...
validate_search_space()#

Validate all fields have valid types and structure.

Return type:

BaseSearchSpace

ConformalConfig#

ConformalConfig controls conformal prediction, which wraps any trained model with distribution-free prediction intervals. It is passed as conformal_params to TwigaForecaster.

Field

Type

Default

Description

method

Literal["residual", "quantile", "residual-fitting"]

required

Conformal prediction method. "residual" uses absolute residuals; "quantile" uses quantile regression scores; "residual-fitting" fits a secondary model to predict residuals.

score_type

Literal["scaled", "unscaled", "res", "sign-res"]

required

Type of nonconformity score. "res" = absolute residuals, "sign-res" = signed residuals, "scaled" = normalized residuals, "unscaled" = raw residuals.

alpha

float

required

Significance level, strictly between 0 and 1. Controls the width of prediction intervals: alpha=0.1 produces 90% intervals, alpha=0.05 produces 95% intervals.

Compatibility Rules#

The validate_field_compatibility() model validator enforces that method and score_type are compatible:

Method

Allowed Score Types

"residual"

"res", "sign-res"

"residual-fitting"

"res", "sign-res"

"quantile"

"scaled", "unscaled"

Attempting to use an incompatible combination raises a ValueError at construction time.

Extreme alpha values

A field validator warns (but does not reject) when alpha < 0.01 (overly wide intervals) or alpha > 0.5 (overly narrow intervals). Both are technically valid but rarely useful in practice.

Example#

from twiga.core.config import ConformalConfig

# 90% prediction intervals using absolute residuals
conformal_config = ConformalConfig(
    method="residual",
    score_type="res",
    alpha=0.1,
)

# 95% intervals using quantile regression with scaled scores
conformal_qr = ConformalConfig(
    method="quantile",
    score_type="scaled",
    alpha=0.05,
)

API Reference#

class twiga.core.config.ConformalConfig(**data)#

Bases: BaseModel

Configuration for conformal prediction methods.

Supports three conformal predictors - residual-based, quantile-based, and residual-fitting - each with compatible nonconformity score types.

Parameters:
  • method (Literal["residual", "quantile", "residual-fitting"], optional) –

    Conformal prediction method:

    • "residual" - nonconformity scores based on absolute residuals |y - ŷ|.

    • "quantile" - quantile regression for prediction intervals.

    • "residual-fitting" - fits a secondary model to predict residuals for adaptive interval widths.

    Defaults to "residual".

  • score_type (str, optional) – Nonconformity score type. "scaled" / "unscaled" for quantile method; "res" / "sign-res" for residual-based methods. Defaults to "res".

  • alpha (float, optional) – Significance level controlling the confidence level (1 - alpha) of the prediction intervals. Must be in (0, 1). For example alpha=0.1 → 90 % coverage. Defaults to 0.1.

Raises:

ValueError – If method="quantile" is combined with a residual score type, or if a residual method is combined with a quantile score type.

Examples

>>> ConformalConfig(method="residual", score_type="res", alpha=0.1)
ConformalConfig(method='residual', score_type='res', alpha=0.1)
>>> ConformalConfig(method="quantile", score_type="scaled", alpha=0.05)
ConformalConfig(method='quantile', score_type='scaled', alpha=0.05)
alpha: Annotated[float, FieldInfo(annotation=NoneType, required=False, default=0.1, description='Significance level for prediction intervals. Controls coverage as (1 - alpha). Example: alpha=0.1 90% prediction intervals.', metadata=[Gt(gt=0.0), Lt(lt=1.0)])]#
method: Annotated[Literal['residual', 'quantile', 'residual-fitting'], FieldInfo(annotation=NoneType, required=False, default='residual', description="Conformal prediction method. 'residual': absolute residual scores. 'quantile': quantile regression intervals. 'residual-fitting': secondary model predicts residuals for adaptive widths.")]#
model_config: ClassVar[ConfigDict] = {'extra': 'forbid'}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

score_type: Annotated[Literal['scaled', 'unscaled', 'res', 'sign-res'], FieldInfo(annotation=NoneType, required=False, default='res', description="Nonconformity score type. 'scaled'/'unscaled': for quantile method. 'res'/'sign-res': for residual-based methods.")]#
validate_method_score_compatibility()#

Validate that method and score_type are compatible.

Return type:

ConformalConfig

classmethod warn_extreme_alpha(v)#

Warn if alpha is likely to produce degenerate intervals.

Return type:

float

Putting It All Together#

The following example demonstrates how all four configuration objects work together in a complete forecasting workflow.

import pandas as pd
from sklearn.preprocessing import StandardScaler, RobustScaler

from twiga.core.config import (
    DataPipelineConfig,
    ForecasterConfig,
    ConformalConfig,
)
from twiga.forecaster.core import TwigaForecaster
from twiga.models.ml.xgboost_model import XGBOOSTConfig
from twiga.models.nn.mlpf_model import MLPFConfig

# --- Data pipeline configuration ---
data_config = DataPipelineConfig(
    target_feature="load_mw",
    period="1h",
    lookback_window_size=168,
    forecast_horizon=48,
    calendar_features=["hour", "dayofweek", "month"],
    exogenous_features=["ghi", "temperature"],
    lags=[1, 24, 48, 168],
    windows=[24, 48],
    window_funcs=["mean", "std"],
    input_scaler=StandardScaler(),
    target_scaler=RobustScaler(),
)

# --- Model configurations ---
xgb_config = XGBOOSTConfig(device="cpu")
mlpf_config = MLPFConfig.from_data_config(data_config, max_epochs=30, batch_size=128)

# --- Training orchestration ---
train_config = ForecasterConfig(
    split_freq="months",
    train_size=6,
    test_size=1,
    window="expanding",
    project_name="EnergyForecast",
    seed=42,
)

# --- Conformal prediction ---
conformal_config = ConformalConfig(
    method="residual",
    score_type="res",
    alpha=0.1,
)

# --- Assemble and run ---
forecaster = TwigaForecaster(
    data_params=data_config,
    model_params=[xgb_config, mlpf_config],
    train_params=train_config,
    conformal_params=conformal_config,
)

data = pd.read_parquet("data/timeseries.parquet")
train_df = data[data.timestamp <= "2024-06-01"]
val_df = data[(data.timestamp > "2024-05-15") & (data.timestamp <= "2024-07-01")]
test_df = data[data.timestamp > "2024-07-01"]

forecaster.fit(train_df=train_df, val_df=val_df)
forecaster.calibrate(calibrate_df=val_df)

predictions_df, metrics_df = forecaster.evaluate_point_forecast(test_df=test_df)
interval_df, interval_metrics = forecaster.evaluate_interval_forecast(test_df=test_df)

See also: TwigaForecaster | Data Pipeline | Backtesting | Hyperparameter Tuning | Conformal Prediction