Skip to content
Ivan Svetunkov edited this page Jun 9, 2026 · 7 revisions

CES - Complex Exponential Smoothing

CES (Complex Exponential Smoothing) is an alternative to ETS that uses complex-valued smoothing parameters. It captures both the level and the "potential" of the time series, making it particularly useful for series with complex seasonal patterns or smooth trends without abrupt changes.

CES is available in both R (ces() / auto.ces()) and Python (CES / AutoCES). The Python port is a direct translation of the R algorithm and produces numerically equivalent results when the same NLopt version (nlopt>=2.10.0) is used on both sides.

Overview

Unlike traditional ETS models that require explicit trend specification, CES uses complex numbers to automatically capture both stationary and non-stationary behaviour. The model estimates complex smoothing parameters a (and b, for partial and full seasonality) that determine the dynamics. The transition is driven in companion-matrix form by the real and imaginary parts of those parameters, so a single complex coefficient encodes both the speed of level adaptation and the "momentum" carried into the next period.

Seasonality Types

Type Code Description
None "none" No seasonality, simple CES
Simple "simple" Lagged CES (uses t-m observation)
Partial "partial" Real seasonal component (additive-like)
Full "full" Complex seasonal component (adaptive)

Python Usage

from smooth import CES, AutoCES
import numpy as np

y = np.array([112, 118, 132, 129, 121, 135, 148, 148, 136, 119, 104, 118,
              115, 126, 141, 135, 125, 149, 170, 170, 158, 133, 114, 140])

# Simple CES (no seasonality)
model = CES(seasonality="none", h=8, holdout=True)
model.fit(y)
fc = model.predict(h=8)

# CES with simple seasonality
model = CES(seasonality="simple", lags=[1, 12], h=18, holdout=True)
model.fit(y)
print(f"a = {model.a_}")        # complex smoothing parameter
print(model.model_name)         # e.g. "CES(simple)"

# CES with partial seasonality (real seasonal component)
model = CES(seasonality="partial", lags=[1, 12], h=18, holdout=True)
model.fit(y)
print(f"b = {model.b_}")        # real-valued for partial

# CES with full seasonality (complex seasonal component)
model = CES(seasonality="full", lags=[1, 12], h=18, holdout=True)
model.fit(y)

# Automatic seasonality selection
auto = AutoCES(lags=[1, 12], h=18, holdout=True, ic="AICc")
auto.fit(y)
print(auto.best_model_.model_name)
fc = auto.predict(h=18)

Note: strict R-parity in the two-stage NLopt path (BOBYQA stage-1 → Nelder-Mead stage-2) requires nlopt>=2.10.0. Older NLopt versions still fit, but the stage-1 trajectory may diverge slightly from R; the Python CES.fit() emits a RuntimeWarning in that case.

R Usage

library(smooth)

# Simple CES (no seasonality)
ces(BJsales, h=8, holdout=TRUE)

# CES with simple seasonality
ces(AirPassengers, h=18, holdout=TRUE, seasonality="simple")

# CES with partial seasonality (additive-like)
ces(AirPassengers, h=18, holdout=TRUE, seasonality="partial")

# CES with full seasonality (most flexible)
ces(AirPassengers, h=18, holdout=TRUE, seasonality="full")

# Automatic seasonality selection
auto.ces(AirPassengers, h=18, holdout=TRUE)

With External Regressors

Note: External regressors (xreg in R, X in Python) are supported in R. The Python CES.fit(y, X=...) argument is wired through to the C++ core but the wider regressors="select" / "adapt" paths are R-only for now.

# CES with explanatory variables (R)
ces(y, h=12, xreg=X, regressors="use")

# Automatic regressor selection (R)
ces(y, h=12, xreg=X, regressors="select")
# CES with explanatory variables (Python — fixed coefficients only)
model = CES(seasonality="partial", lags=[1, 12], h=12)
model.fit(y, X=X)
fc = model.predict(h=12, X=X_future)

Parameters

Parameter Type (R) Type (Python) Default Description
y vector/ts NDArray - Time series data
seasonality character str (Literal) "none" Seasonality type: "none" / "simple" / "partial" / "full"
lags numeric vector List[int]/None frequency(y) (R) / [1] (Python) Seasonal lag(s)
a complex complex/None NULL First smoothing parameter (estimated if NULL/None)
b complex complex/float/None NULL Second smoothing parameter (estimated if NULL/None; real for partial, complex for full)
initial character str "backcasting" Initialisation method: "backcasting" / "optimal" / "two-stage" / "complete"
loss character str "likelihood" Loss function
h integer int/None 0 Forecast horizon
holdout logical bool FALSE Use holdout validation
bounds character str "admissible" "admissible" (eigenvalue stability) or "none"
ic character str "AICc" Information criterion (AutoCES only in Python)
xreg (R) / X (Python) matrix NDArray/DataFrame NULL/None External regressors
regressors character str "use" How to handle regressors ("use" / "select"; only "use" in Python)

Model Selection with auto.ces / AutoCES

auto.ces() (R) and AutoCES (Python) fit the candidate seasonality types in turn and select the best by information criterion. Both prune the seasonality pool by sample size before fitting (e.g. "full" is skipped if n ≤ 2*frequency + 7).

# R: automatic selection between seasonality types
auto.ces(y, h=12, holdout=TRUE)

# R: specify which seasonality types to try
auto.ces(y, h=12, seasonality=c("none", "simple", "partial", "full"))
# Python: automatic selection
AutoCES(lags=[1, 12], h=12, holdout=True).fit(y)

# Python: restrict the pool
AutoCES(seasonality=["none", "partial"], lags=[1, 12], h=12).fit(y)

Complex Parameters Interpretation

The complex smoothing parameters a = a_r + i·a_i have the following interpretation:

  • Real part (a_r): Controls the speed of adaptation to level changes
  • Imaginary part (a_i): Controls the "momentum" or potential energy in the system

The interplay between real and imaginary parts allows CES to capture both trending and oscillating behaviours without explicit trend specification.

Advantages Over ETS

  1. No explicit trend selection: CES automatically adapts to trends
  2. Fewer model variants: Only 4 seasonality types vs 30 ETS models
  3. Long memory processes: Performs well on time series without abrupt changes; has the long-memory property.

Fitted Attributes

Element (R) Element (Python) Type (R) Type (Python) Description
modelName() model_name character str Full model name (e.g. "CES(partial)", "CESX(full)")
parameters$a a_ complex complex / list[complex] First smoothing parameter (list for simple seasonality with multiple lags)
parameters$b b_ complex / numeric complex / float / list Second smoothing parameter (partial/full only)
persistence persistence_vector complex vector NDArray Persistence vector built from Re(a) ± Im(a)
transition transition_matrix matrix NDArray Companion-form transition matrix F
measurement measurement_matrix matrix NDArray Measurement matrix W
initial initial_states_ list Dict[str, NDArray] Initial state values ("nonseasonal" / "seasonal" / "xreg" keys)
states states matrix NDArray State matrix over time
fitted fitted vector NDArray Fitted values
residuals residuals vector NDArray Residuals
forecast predict() result vector NDArray (via .mean) Point forecasts
logLik loglik numeric float Log-likelihood
AIC / AICc / BIC / BICc aic / aicc / bic / bicc numeric float Information criteria
scale scale_ numeric float Residual scale
B B / coef vector NDArray All estimated parameters
nParam n_param numeric int Number of estimated parameters
ICs (auto.ces) ICs (AutoCES) numeric vector Dict[str, float] Information criteria for each candidate seasonality

For AutoCES, the best fitted CES instance is exposed as .best_model_ (and .predict() / .summary() delegate to it), mirroring R's auto.ces returning the chosen ces object.

Relationship to ADAM

CES is mathematically distinct from the ETS family covered by ADAM and ES: the smoothing parameters are complex-valued and the state evolves through a companion-form transition matrix rather than the additive/multiplicative ETS recursion. It still uses the same C++ adamCore engine for state-space filtering and forecasting in the Python port (the _adamCore pybind module), which is why fitted attributes such as states / transition line up with the rest of the package.

References

  • Svetunkov, I., Kourentzes, N., & Ord, J.K. (2022). Complex exponential smoothing. Naval Research Logistics, 69(5), 697-717. DOI: 10.1002/nav.22074

See Also

Related Functions

  • ADAM - Main unified framework
  • ES - Traditional Exponential Smoothing
  • GUM - Generalised Univariate Model

Parameter Documentation

Clone this wiki locally