Skip to content

Instantly share code, notes, and snippets.

@nehalecky
Created November 5, 2025 16:10
Show Gist options
  • Select an option

  • Save nehalecky/225166035829b7db6529ba74014646a7 to your computer and use it in GitHub Desktop.

Select an option

Save nehalecky/225166035829b7db6529ba74014646a7 to your computer and use it in GitHub Desktop.
Darts Forecasting Model Style Guide

Darts Forecasting Model Style Guide

Reference guide extracted from CONTRIBUTING.md and existing model implementations Created: 2025-11-05

Core Principles (from CONTRIBUTING.md)

  1. Focus on simplicity and clarity for end users
  2. Design the best possible API, simple by default
  3. Make it hard for users to make mistakes
  4. Better write clear code than fancy code
  5. Use TimeSeries as the main interface (users shouldn't worry about numpy/torch internals)

These principles prevail even if they mean more code inside the library.

Code Organization

Prefer Inline Over Abstraction

  • Keep logic in the model class itself
  • Avoid helper utilities unless truly necessary
  • Don't create abstraction layers for simple operations
  • Use parent class methods (_build_forecast_series(), etc.)

Examples:

  • YES: raise_if_not(condition, "message", logger) inline
  • NO: Separate validation.py module with wrapper functions
  • YES: Device handling via pl_trainer_kwargs (PyTorch Lightning)
  • NO: Custom device_utils.py with auto-detection

File Structure

  • Single model per file is typical
  • Shared utilities only if used by multiple models
  • Keep model files self-contained when possible

Docstring Style

Format (NumPy/Darts convention)

def __init__(self, param: int):
    """Short Title

    Brief description in 1-3 paragraphs explaining what it does.

    Parameters
    ----------
    param
        Description of parameter (no type - already in signature)

    Examples
    --------
    >>> from darts.models import MyModel
    >>> model = MyModel(param=10)
    >>> model.fit(series)
    >>> pred = model.predict(6)

    References
    ----------
    .. [1] Author. (Year). Paper title.
    """

Length Guidelines

  • Focus on clarity, not brevity - explain what users need to know
  • Working examples are critical - show actual usage
  • Parameters section should be complete but concise
  • Line count is not a hard limit - 20-100 lines is fine if it serves users

What to Include

  • What the model does (conceptual explanation)
  • Key parameters with clear descriptions
  • Runnable examples (critical!)
  • References to papers (if applicable)
  • Important notes/caveats

What to Avoid

  • Implementation details (how it works internally)
  • Excessive architecture explanations
  • Tutorial content (belongs in notebooks)

Method Signatures

__init__() Pattern

def __init__(
    self,
    # Model-specific parameters first
    context_length: int,
    forecast_horizon: int,
    # Standard Darts parameters
    output_chunk_shift: int = 0,
    **kwargs,  # Pass to parent
):
    super().__init__(**kwargs)
    self.context_length = context_length
    self.forecast_horizon = forecast_horizon

Key points:

  • Model-specific params first, Darts standard params after
  • Use **kwargs to pass parent class parameters
  • No need to store all kwargs - only what the model uses

fit() Pattern

def fit(
    self,
    series: Union[TimeSeries, Sequence[TimeSeries]],
    past_covariates: Optional[Union[TimeSeries, Sequence[TimeSeries]]] = None,
    future_covariates: Optional[Union[TimeSeries, Sequence[TimeSeries]]] = None,
    **kwargs
) -> "MyModel":
    """Fit the model to data.

    Parameters
    ----------
    series
        Training time series
    past_covariates
        Past covariates (optional)
    future_covariates
        Future covariates (optional)

    Returns
    -------
    self
        Fitted model
    """
    super().fit(series)

    # Validate inputs inline
    raise_if_not(
        condition,
        "Clear error message",
        logger,
    )

    # Model-specific fitting logic
    ...

    return self

predict() Pattern

def predict(
    self,
    n: int,
    series: Optional[TimeSeries] = None,
    past_covariates: Optional[TimeSeries] = None,
    future_covariates: Optional[TimeSeries] = None,
    num_samples: int = 1,
    **kwargs
) -> TimeSeries:
    """Generate forecast.

    Parameters
    ----------
    n
        Forecast horizon
    series
        Input series (optional if fitted)
    num_samples
        Number of samples for probabilistic forecasting

    Returns
    -------
    TimeSeries
        Forecasted values
    """
    super().predict(n, num_samples)

    # Prediction logic - direct implementation
    forecast = ...

    return self._build_forecast_series(forecast)

Error Handling

Use Darts Utilities

from darts.logging import get_logger, raise_if, raise_if_not, raise_log

logger = get_logger(__name__)

# In code:
raise_if_not(
    condition,
    "Error message with context",
    logger,
)

raise_if(
    bad_condition,
    "Why this is a problem",
    logger,
)

Pattern: Validate inline with raise_if/raise_if_not, not separate validation methods.

Device Management (PyTorch Models)

Standard Approach

Device is NOT an __init__ parameter. Use PyTorch Lightning's trainer kwargs:

# User code
model = MyModel(
    input_chunk_length=12,
    pl_trainer_kwargs={"accelerator": "gpu", "devices": [0]}
)

Don't create custom device detection/management - Lightning handles it.

Properties vs Methods

Use @property for:

  • Capabilities: supports_multivariate, supports_probabilistic_prediction
  • Metadata: min_train_series_length, extreme_lags
  • Computed attributes that don't change: model_name
@property
def supports_multivariate(self) -> bool:
    return True

Use methods for:

  • Actions with side effects
  • Operations taking parameters
  • Anything that does computation

Imports

Standard Order

# 1. Standard library
from typing import Optional, Union, Sequence

# 2. Third-party
import numpy as np
import torch

# 3. Darts
from darts import TimeSeries
from darts.logging import get_logger, raise_if_not
from darts.models.forecasting.forecasting_model import GlobalForecastingModel

logger = get_logger(__name__)

Lazy Imports (optional dependencies)

def _load_pretrained_model(self):
    try:
        import optional_package
    except ImportError:
        raise ImportError(
            "The 'optional_package' is required.\n"
            "Install with: pip install 'darts[extra]'"
        )

What Makes Code "AI Generated / Vibe Coded"

Anti-Patterns to Avoid

Unnecessary abstraction layers

# Don't
def fit(self, ...):
    if self.mode == "A":
        return self._fit_mode_a(...)
    else:
        return self._fit_mode_b(...)

# Do
def fit(self, ...):
    if self.mode == "A":
        # Logic here directly
        pass
    else:
        # Other logic here
        pass

Helper modules for simple operations

# Don't: validation.py with validate_series(), validate_params(), etc.
# Do: Inline with raise_if_not()

Registry/metadata systems for model info

# Don't: registry.yaml + get_model_spec()
# Do: @property methods in model class

Over-documenting implementation details

# Don't: Explain architecture, encoder-decoder, attention mechanisms
# Do: "This model uses a transformer architecture [1]_" + reference

The Darts Way

Direct Implementation

  • Implement logic directly in methods
  • Use parent class utilities (_build_forecast_series, etc.)
  • Validate inline with raise_if_not

Minimal Surface Area

  • Expose only what users need
  • Keep internals private (_method_name)
  • Use standard Darts parameter names

User-Focused Documentation

  • Show how to use it (examples)
  • Explain what it does (not how)
  • Reference papers for details

Trust PyTorch Lightning

For torch models:

  • Device management via pl_trainer_kwargs
  • Training loop via Lightning
  • Don't reimplement what Lightning provides

Summary

Darts style = Simple, direct, user-focused

Write code that:

  1. Is easy for users to understand and use
  2. Validates clearly with inline checks
  3. Implements logic directly without unnecessary abstraction
  4. Uses parent class utilities
  5. Documents usage, not implementation
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment