Reconciliation tools

This module provides utility classes for forecast reconciliation, with support for both cross-sectional and temporal frameworks. The tools in FoRecoPy are built around structured matrices that represent aggregation relationships and linear constraints, which serve as the foundation for different reconciliation methods. Two complementary toolsets are available:

  • cstools(): For hierarchically, grouped, or otherwise linearly constrained series observed at the same frequency.

  • tetools(): For temporal hierarchies, where the same time series can be aggregated or linearly combined at multiple frequencies (e.g., monthly, quarterly, yearly). This allows one to reconcile forecasts across frequencies so that, for example, the sum of 12 monthly forecasts matches the corresponding annual forecast.

class cstools(agg_mat: Array = None, cons_mat: Array = None)[source]

Cross-sectional reconciliation tools

Utilities for working with linearly constrained (hierarchical/grouped) multivariate time series in a cross-sectional framework.

This class encapsulates standard matrices used in forecast reconciliation: the aggregation matrix \(\mathbf{A}\), the structural matrix \(\mathbf{S}\), and the zero-constraints matrix \(\mathbf{C}\). Given either \(\mathbf{A}\) or \(\mathbf{C}\) at construction, the remaining matrices and dimension metadata are derived.

Parameters

agg_mat: ndarray

A (\(n_a \times n_b\)) numeric matrix representing the cross-sectional aggregation matrix. It maps the \(n_b\) bottom-level (free) variables into the \(n_a\) upper (constrained) variables.

cons_mat: ndarray

A (\(n_a \times n\)) numeric matrix representing the cross-sectional zero constraints. It spans the null space for the reconciled forecasts.

Attributes

agg_mat: ndarray

The aggregation matrix \(\mathbf{A}\) (may be inferred from \(\mathbf{C}\)).

dim: tuple

Dimension summary:

  • if \(\mathbf{A}\) given: \((n, n_a, n_b)\) with \(n = n_a + n_b\).

  • if \(\mathbf{C}\) given: \((n, r, n - r)\) with \(r = \text{ rank/rows of } \mathbf{C}\).

_cons_mat: ndarray

The constraints matrix \(\mathbf{C}\), built on demand if \(\mathbf{A}\) is provided.

_strc_mat: ndarray

The structural matrix \(\mathbf{S} = [\mathbf{A}'\quad\mathbf{I}_{n_b}]'\).

Methods

strc_mat() -> jnp.ndarray

Returns the temporal structural matrix \(\mathbf{S}\). Requires agg_mat to be available.

cons_mat() -> jnp.ndarray

Returns the temporal constraints matrix \(\mathbf{C}\).

Notes

  • Shapes:

    • \(\mathbf{A}\) has shape \((n_a, n_b)\).

    • \(\mathbf{S}\) has shape \((n_a + n_b, n_b) = (n, n_b)\).

    • \(\mathbf{C}\) has shape \((n_a, n) = (n_a, n_a + n_b)\) when built from \(\mathbf{A}\).

  • When initialized with cons_mat, \(\mathbf{A}\) is only inferred if the leading \((r\times r)\) block equals the identity; specifically, if \(\mathbf{C} = [\mathbf{I}_{r}\quad-\mathbf{A}]\).

See Also

cscov csrec

Examples

>>> import jax.numpy as jnp
>>> import forecopy as rpy
>>> # One-level hierarchy: Z = X + Y
>>> A = jnp.array([[1., 1.]])            # shape (n_a=1, n_b=2)
>>> tools = rpy.cstools(agg_mat=A)
>>> tools.dim
(3, 1, 2)
>>> S = tools.strc_mat()
>>> S.shape                              # [A' ; I_2]'
(3, 2)
>>> C = tools.cons_mat()
>>> C                                    # [I_1  -A]
Array([[ 1., -1., -1.]], dtype=float32)
>>>
>>> # Start from constraints: C = [1, -1, -1]
>>> C = jnp.array([[1., -1., -1.]])      # r=1, n=3
>>> tools2 = rpy.cstools(cons_mat=C)
>>> tools2.dim
(3, 1, 2)
>>> tools2.agg_mat                       # inferred because C = [I | -A]
Array([[1., 1.]], dtype=float32)
>>> tools2.strc_mat().shape
(3, 2)
class tetools(agg_order: list | int, tew: str = 'sum', fh: int = 1)[source]

Temporal reconciliation tools

Utilities for forecast reconciliation through temporal hierarchies.

Given a set of temporal aggregation orders, this class constructs the temporal aggregation matrix (linear-combination matrix) and provides the corresponding structural and zero-constraint matrices used in temporal reconciliation.

Parameters

agg_order: list | int

Highest available sampling frequency per seasonal cycle (max. order of temporal aggregation, \(m\)), or a list representing a subset of \(p\) factors of \(m\).

tew: str, default sum

Temporal aggregation weighting scheme applied within each high-to-low block when building the aggregation matrix:

  • sum: sum over the block.

  • avg: arithmetic average over the block.

  • last: take the last element in the block.

  • first: take the first element in the block.

fh: int, default 1

Forecast horizon for the lowest frequency (most temporally aggregated) series.

Attributes

m: int

Maximum aggregation order, m = max(agg_order).

kset: list[int]

Decreasing set of temporal aggregation orders that divide m. If a single integer was supplied to agg_order, this equals the full list of positive factors of m.

p: int

Number of elements in kset.

ks: int

Partial sum of factors \(k^\ast\), defined as sum(m / kset[:-1]).

kt: int

Total sum of factors \(k_t\), defined as sum(m / kset).

tew: str

The requested temporal weighting flag (see parameter description).

_agg_mat: jnp.ndarray

Temporal aggregation matrix \(\mathbf{A}\).

_strc_mat: jnp.ndarray or None

Temporal structural matrix, \(\mathbf{S} = [\mathbf{A}'\quad\mathbf{I}_{m}]'\).

_cons_mat: jnp.ndarray or None

Temporal zero-constraints matrix, \(\mathbf{C} = [\mathbf{I}_{k^\ast}\quad-\mathbf{A}]\).

Methods

strc_mat() -> jnp.ndarray

Return the temporal structural matrix \(\mathbf{S}\).

cons_mat() -> jnp.ndarray

Return the temporal constraints matrix \(\mathbf{C}`\).

Notes

  • Shapes:
    • \(\mathbf{A}\) has shape \((k^\ast, m)\).

    • \(\mathbf{S}\) has shape \((k^\ast + m, m) = (k_t, m)\).

    • \(\mathbf{C}\) has shape \((k^\ast, k_t)\).

See Also

tecov terec

Examples

>>> import jax.numpy as jnp
>>> import forecopy as rpy
>>>
>>> # Quarterly over monthly (m = 4), full factor set {4,2,1}
>>> obj = rpy.tetools(agg_order=4, tew='sum', fh=1)
>>> obj.kset
[4, 2, 1]
>>> A = obj._agg_mat
>>> S = obj.strc_mat()
>>> C = obj.cons_mat()
>>> A.shape, S.shape, C.shape
((3, 4), (7, 4), (3, 7))
>>>
>>> # Custom set (divisors of m=12 that you care about): {12, 6, 3, 1}
>>> obj2 = rpy.tetools(agg_order=[12, 6, 3, 1], fh=2)
>>> obj2.kset
[12, 6, 3, 1]
>>> obj2._agg_mat.shape      # i.e., (2+4+24, 12) = (30, 12)
( (12/6)*2 + (12/3)*2 + (12/1)*2, 12 )