autots.evaluator.tva package¶
Submodules¶
autots.evaluator.tva.coherence module¶
Post-forecast coherence correction for the TVA factor trend mode.
Pulls per-series trend forecasts back toward their shared factor consensus,
as a selected correction (never assumed) applied to the trend block only —
seasonality/holidays/anomalies/level-shifts are the caller’s to re-add
untouched. Loading signs are factor-model-indeterminate, so resolve_signs
picks an orientation from the whole panel (not one series) and reports a
confidence that gates whether coherence_shrink may act on that factor.
strength=0.0 is always a bitwise identity, and nothing here raises —
degenerate inputs (one series, all-zero/NaN loadings, empty or singular
graphs) just return the input unchanged.
- autots.evaluator.tva.coherence.apply_selection(trend_fc, selection, graphs, config=None)¶
Apply a
select_coherenceresult and report what it actually did.adjustment_rmsmatters because a selected graph/strength that moves the forecast by ~0 is a no-op wearing a hat; the diagnostics should say so.- Parameters:
trend_fc – (H, N) standardized trend forecast.
selection – a
select_coherenceresult (orNone).graphs – the same
{name: graph}mapping the selection ran over.config – overrides for
DEFAULT_COHERENCE_CONFIG.
- Returns:
(adjusted, info).adjustedis the input itself when nothing was selected.infois JSON-safe withgraph,strength,applied,sign_confidence,skipped_groups,n_links,adjustment_rms,mase_costandcoherence_gain.
- autots.evaluator.tva.coherence.block_adjacency(graph, n_series: int) ndarray¶
(N, N) block-constant adjacency for a
group_graphresult.A group is exactly a clique of
+1links, so this makes the group and Laplacian views share one solver instead of two.- Parameters:
graph – a
group_graphresult dict, or a plain{key: [indices]}.n_series –
- Returns:
(N, N) symmetric hollow adjacency of 0.0 / 1.0.
- autots.evaluator.tva.coherence.build_candidates(loadings, config=None, prior=None)¶
Convenience: the candidate graph set implied by
config['graph'].Wiring helper so
tva.pybuilds the same candidate set for the inner selection and for the final application with one call each.When a
prioris supplied andconfig['prior_weight'] > 0, the set gains a prior-blendedlaplacian_k{k}_priorper neighbour count plus aprior_onlyadjacency. They are only candidates:select_coherencegrades them on held-out inner origins against the unblended graphs and the always-admissible “do nothing” baseline, so a wrong prior costs nothing.- Parameters:
loadings – (N, K) loading matrix.
config – overrides for
DEFAULT_COHERENCE_CONFIG.graphselects the family:'group','laplacian','auto'(both) or'none'(empty, i.e. the correction is disabled).prior – optional (N, N) signed user prior.
Nonereproduces the candidate dict exactly as before.
- Returns:
(graphs, meta)wheregraphsis{name: graph}andmetacarries the JSON-safesign_confidenceandsigns.
- autots.evaluator.tva.coherence.coherence_shrink(trend_fc, graph, strength, weights=None, config=None)¶
Pull standardized trend changes toward their graph consensus.
Minimizes, independently at each horizon step,
||dx - dx0||^2_W + strength * sum_ij |A_ij| (s_ij * dx_i - dx_j)^2where
dxis each series’ cumulative trend change from the forecast anchor,s_ij = sign(A_ij), andW = diag(weights). This reduces to the signed graph LaplacianL = diag(sum_j |A_ij|) - Aand normal equations(W + strength * L) dx = W dx0— one symmetric solve per forecast, shared across horizon steps sinceW/Ldon’t depend on h.Shrinks changes, not levels, so the forecast anchor (row 0, where
dx0 = 0) is structurally preserved for the safety layer’s re-anchoring to own. The group form is this same solve on a block-constant adjacency (block_adjacency), so both views share one solver.- Parameters:
trend_fc – (H, N) standardized trend forecast only — seasonality, holidays, anomalies and level-shift intercepts must not be in here; the factor structure has no claim on them.
graph – a
group_graphdict or an (N, N) signed adjacency.strength – shrinkage weight.
0.0returns the input unchanged, bitwise.weights – optional (N,) precision weights, overriding the graph’s.
config – overrides for
DEFAULT_COHERENCE_CONFIG.
- Returns:
(H, N) adjusted trend forecast. The input itself on any degenerate case: fewer than two series, an empty or all-zero graph, a non-finite panel, or a solve that fails.
- autots.evaluator.tva.coherence.group_graph(loadings, config=None) dict¶
Block-structured graph: one group per (dominant factor, loading sign).
Grouping by dominant factor and sign keeps every in-group link a
+1link — series loading the same factor with opposite signs are supposed to diverge, not be pulled together. Factors belowmin_sign_confidenceare dropped: without a trusted orientation, “same sign” isn’t meaningful.dominance_marginandmin_loading_sharelet a series abstain when its dominant factor isn’t decisive; both default to exact no-ops.- Parameters:
loadings – (N, K) loading matrix (raw; signs are resolved internally).
config – overrides for
DEFAULT_COHERENCE_CONFIG. Readsgated(series indices to exclude),sigma,min_sign_confidence,dominance_marginandmin_loading_share.
- Returns:
dict with
groups:{group_key: [series indices]}, keys are strings'f{k}+'/'f{k}-'so the result is JSON-safe.weights: (N,) precision weights.confidence: (K,) per-factor sign confidence.signs: (K,) resolved orientation per factor.kind:'group'.
- autots.evaluator.tva.coherence.laplacian_graph(loadings, config=None, n_neighbors: int = 5, prior=None) ndarray¶
Signed k-nearest-neighbour graph over sign-resolved loading vectors.
General form for panels with no clean dominant-factor partition (overlapping exposures, more factors than the group view expresses). Links are cosine similarity of loading vectors (+1 same profile, -1 opposite), keeping each node’s
n_neighborsstrongest before symmetrizing — negative links stay meaningful so anti-correlated pairs get pulled apart, not together.Series below
min_sign_confidenceon their dominant factor, and series ingated, are isolated (links zeroed) at build time.- Parameters:
loadings – (N, K) loading matrix.
config – overrides for
DEFAULT_COHERENCE_CONFIG.n_neighbors – links retained per node before symmetrization.
prior – optional (N, N) signed user prior, blended into the cosine similarity at
config['prior_weight']before the kNN step. Both are signed, so a negative prior link pushes apart natively.
- Returns:
(N, N) signed, symmetric, hollow weighted adjacency. All-zero when the graph is degenerate, which every consumer reads as “do nothing”.
- autots.evaluator.tva.coherence.net_direction(paths: ndarray, window: int = None) ndarray¶
(N,) sign of the windowed end-to-end change of each column.
Windowed rather than last-minus-first so a single noisy endpoint can’t decide the direction. A genuinely flat column returns exactly 0.
- autots.evaluator.tva.coherence.resolve_signs(loadings, factors=None, stability=None)¶
Orient each factor by its loading mass, and report the margin.
Unlike the single-largest-loading convention, this votes with every series (
sign(sum_i lambda_ik * |lambda_ik|)), so one outlier loading can’t flip the orientation. The margin of that vote becomes the confidence, so a near-50/50 factor can be flagged as having no reliable orientation.- Parameters:
loadings – (N, K) loading matrix (or (N,) for a single factor).
factors – optional (T, K) factor paths, used only to break an exact zero-mass tie via the factor’s own net drift.
stability – optional (K,) per-factor stability in [0, 1], multiplied into the confidence.
- Returns:
(signed_loadings, confidence)— the (N, K) loadings with each column multiplied by its resolved sign, and a (K,) array of|sum(w * lambda)| / sum(|w * lambda|)in [0, 1].
- autots.evaluator.tva.coherence.select_coherence(trend_folds, actual_folds, graph_candidates, scale, config=None) dict¶
Choose
(graph, strength)on inner rolling origins. Accuracy vetoes.Two stages: (1) reject any candidate whose pooled scaled MAE exceeds the
strength=0baseline by more thanmase_guardrail(accuracy is the floor, not something coherence should be traded for), then (2) among survivors minimize trend-only coherence error, ties going to the smaller strength.strength=0.0is always admissible, so “do nothing” is always reachable.- Parameters:
trend_folds – list of (H, N) standardized trend forecasts, one per inner origin.
actual_folds – list of (H, N) realized values aligned to those origins, in the same standardized space.
graph_candidates –
{name: graph}, each agroup_graphdict or an adjacency array.scale – (N,) MASE denominators in the same standardized space.
config – overrides for
DEFAULT_COHERENCE_CONFIG.
- Returns:
dict with
graph(name or None),strength,baseline_mae,baseline_coherence_error,selected_mae,selected_coherence_error,n_admissible,reasonand a JSON-safetableof every candidate evaluated.
autots.evaluator.tva.continuation module¶
Validation-selected factor continuation (plan item 1a).
Replaces the single damped local-linear extrapolation rule with a candidate set (constant, damped, ridge_ar, regime) chosen per factor by held-out reconstruction error over inner origins, never in-sample fit — an in-sample criterion always prefers the wiggliest continuation.
Pure numpy: nothing here imports torch, so the candidate maths is testable without fitting a model.
- autots.evaluator.tva.continuation.apply_choice(paths: ndarray, origins: ndarray, horizon: int, choice: dict, specs: list = None, knot_times: ndarray = None, coef: ndarray = None, model_deltas: ndarray = None, config: dict = None) ndarray¶
(O, H, K) future deltas for an already-selected per-factor choice.
- autots.evaluator.tva.continuation.build_specs(config: dict = None) list¶
Enumerate candidate continuation specs.
Order is deliberate: model’s own rule first, then the constant null, so a tie during selection resolves toward the incumbent and then toward doing nothing.
- Returns:
List of dicts, each with a
kindand a stablename.
- autots.evaluator.tva.continuation.candidate_futures(paths: ndarray, origins: ndarray, horizon: int, specs: list, knot_times: ndarray = None, coef: ndarray = None, model_deltas: ndarray = None) dict¶
{(factor_index, spec_name): (O, H) deltas} for every candidate.
model_deltasis the incumbent extrapolator’s (O, H, K) deltas (lives in the torch model, so supplied by the caller); backs themodelspec.
- autots.evaluator.tva.continuation.continue_factor(path: ndarray, origins: ndarray, horizon: int, spec: dict, knot_times: ndarray = None, coef_k: ndarray = None) ndarray¶
Future deltas of one factor, relative to its value at each origin.
Deltas (not levels) so every candidate is anchored identically — an anchoring difference would otherwise masquerade as a slope difference during selection.
- Returns:
(O, H) array of deltas.
- autots.evaluator.tva.continuation.select_continuations(paths: ndarray, origins: ndarray, horizon: int, score_fn, specs: list = None, knot_times: ndarray = None, coef: ndarray = None, model_deltas: ndarray = None, config: dict = None) dict¶
Choose a continuation per factor by held-out reconstruction error.
Coordinate descent: factors are not independent once loadings mix them, so each factor’s candidate is scored with the others held at their current choice, and the sweep repeated (default 2 passes; the objective is monotone non-increasing by construction).
- Parameters:
score_fn – callable taking (O, H, K) future deltas, returning a scalar loss (lower is better) — reconstruction into series space, lag weights, loadings and MASE scaling happen here, deliberately outside this module so the selection logic stays pure.
- Returns:
spec_name}),
score(final loss),baseline_score(the incumbent’s loss) andscores(per-factor per-candidate losses from the final pass).- Return type:
dict with
choice({factor_index
autots.evaluator.tva.covariance module¶
Forecast covariance (Sigma) assembly for TVA.
One cross-series forecast-error covariance object, built where the residuals already exist, serving three callers that otherwise each invent their own weighting:
MinT reconciliation, which currently falls back to
W = Iin the default forecasting modes because no residual matrix is available there;the closed-form what-if solver in
autots.evaluator.tva.scenario, whose minimum-disruption update is a Gaussian conditioning step under Sigma;interval coherence (later — nothing here consumes it yet).
The estimator is a shrinkage blend of an empirical covariance and a low-rank structural target:
Sigma = (1 - alpha) * Sigma_emp + alpha * Sigma_struct
Sigma_struct = beta * (Lambda Sigma_f Lambda') + diag(psi)
with alpha the Ledoit-Wolf shrinkage intensity of the residual matrix.
beta is a single least-squares scalar fitted on the off-diagonals, which
is what makes the same assembly usable with loadings that do not live in the
residuals’ units (the trend_network='none' path takes its loadings from
structure discovery, where they are in standardized-difference units). In
'factor' mode Lambda is already in the residual metric and beta lands
near 1, reducing the expression to the plain Lambda Sigma_f Lambda' +
diag(psi) factor-model target. psi is then set residually, so
diag(Sigma_struct) == diag(Sigma_emp) exactly and the blend only ever
changes the off-diagonal (correlation) structure.
- autots.evaluator.tva.covariance.apply_variance_floor(sigma: ndarray, floor_sd: ndarray) tuple¶
Raise
diag(sigma)tofloor_sd**2without distorting correlations.The idiosyncratic term in the factor model is capped at 2 degrees of freedom by design, which is why
factor_variance_sharereads 1.00 on every panel including factor-free ones: the model’s own residual spread is structurally over-confident. Floor the variances at the same sigma the shipped prediction intervals use, rescaling rows and columns so the correlation matrix is untouched and PSD-ness is preserved.- Parameters:
sigma – (N, N) covariance.
floor_sd – (N,) minimum standard deviation per series.
- Returns:
(sigma_floored, binding)wherebindingis an (N,) bool array marking the series whose variance the floor actually raised.
- autots.evaluator.tva.covariance.assemble_covariance(residuals: ndarray, loadings: ndarray = None, factor_var: ndarray = None, scale: ndarray = None, jacobian: ndarray = None, floor_sd: ndarray = None) tuple¶
Assemble the forecast covariance from a residual matrix and a factor model.
- Parameters:
residuals – (n_samples, N) rolling-origin forecast residuals, in the space the model was fit in (normalized, for
'factor'mode).loadings – optional (N, K) factor loadings for the structural target. Without them the estimate is the shrunk empirical covariance alone.
factor_var – optional (K,) factor forecast-error variances; required when
loadingsis given.scale – optional (N,) per-series scale mapping the model space back to raw units.
jacobian – optional (N,) additional per-series derivative, used for the signed-log1p modeling space (
d level / d log-space).floor_sd – optional (N,) raw-unit standard-deviation floor.
- Returns:
(sigma, info)— the (N, N) raw-unit covariance and a diagnostics dict withalpha,beta,psi,n_samples,floor_bindingandhas_structure.
- autots.evaluator.tva.covariance.damped_accumulated_variance(phi: ndarray, horizon: int) ndarray¶
Per-factor forecast-error variance of a damped random walk, horizon-averaged.
A factor continued with damping
phiaccumulates unit innovations ase_h = sum_{j=1..h} phi^(j-1) eps_j, soVar(e_h) = (1 - phi^(2h)) / (1 - phi^2). The residual matrix this is compared against pools every horizon step 1..H into one sample set, so the matching summary is the mean overh, not the terminal value.- Parameters:
phi – (K,) damping coefficients in [0, 1).
horizon – forecast horizon H.
- Returns:
(K,) mean-over-horizon accumulated variance per factor.
- autots.evaluator.tva.covariance.ledoit_wolf_shrinkage(X: ndarray) tuple¶
Ledoit-Wolf shrunk covariance and the shrinkage intensity that made it.
Reproduces
autots.tools.hierarchial.ledoit_wolf_covariance()(there is a test asserting they agree) while also returninggamma, which that function computes and discards. The intensity is the piece this module actually needs: it is a data-driven statement about how badly conditioned the sample covariance is, which is exactly the question “how far should we lean on a structural target instead”.- Parameters:
X – (n_samples, n_features) residual matrix.
- Returns:
(cov, gamma)— the (n_features, n_features) shrunk covariance and the shrinkage intensity in [0, 1].
- autots.evaluator.tva.covariance.nearest_psd(sigma: ndarray, ridge: float = 1e-10) ndarray¶
Symmetrize, clip negative eigenvalues to zero, add a relative ridge.
- autots.evaluator.tva.covariance.structural_target(sigma_emp: ndarray, loadings: ndarray, factor_var: ndarray) tuple¶
Low-rank structural covariance target matched to
sigma_emp’s diagonal.- Parameters:
sigma_emp – (N, N) empirical covariance.
loadings – (N, K) factor loadings, any consistent scaling.
factor_var – (K,) factor forecast-error variances.
- Returns:
(sigma_struct, beta, psi).
autots.evaluator.tva.decomposition module¶
NornDecomposer — Wraps TimeSeriesFeatureDetector for TVA decomposition.
In the spirit of structural weaving, this class decomposes time series into constituent threads (trend, seasonality, noise, etc.) for joint forecasting.
- class autots.evaluator.tva.decomposition.NornDecomposer(detector_params: dict = None, holiday_country=None, holiday_countries: dict = None)¶
Bases:
objectWraps TimeSeriesFeatureDetector to extract structured component DataFrames.
Produces trend, seasonality, holidays, level_shifts, anomalies, and noise as aligned DataFrames sharing the same index and columns as the input.
- Parameters:
detector_params – Dict of kwargs passed to TimeSeriesFeatureDetector.__init__.
- fit(df: DataFrame) NornDecomposer¶
Fit the feature detector on a wide DataFrame.
- Parameters:
df – Wide DataFrame with DatetimeIndex and one numeric column per series.
- Returns:
self
- get_components() dict¶
Extract decomposition components as DataFrames.
- Returns:
Dict with keys ‘trend’, ‘seasonality’, ‘holidays’, ‘level_shifts’, ‘anomalies’, ‘noise’. Each value is a DataFrame (T, N) aligned with the original input index and columns.
- get_features() dict¶
Return the full detected features dict from the underlying detector.
Includes changepoints, holiday impacts, anomaly records, etc. Useful for constructing priors or inspecting detection results.
- get_forecast_components(forecast_length: int) dict¶
Forward-project seasonality, holidays, level shifts, and trend.
Uses the detector’s built-in forecast method to project known deterministic components into the future.
- Parameters:
forecast_length – Number of future periods to project.
- Returns:
Dict with same keys as get_components(), each a DataFrame of shape (forecast_length, N).
- get_residual_sigma()¶
Per-series one-step residual sigma from the underlying detector.
Exposes the detector’s own reconstruction-residual sigma (the same quantity its OLS prediction intervals are built from) so TVA can add it in quadrature to the network’s learned sigma. Returns an (N,) float array aligned to the fitted columns, or None when unavailable.
autots.evaluator.tva.discovery module¶
TVA structure discovery — factors first, conditional edges second.
Torch-free (numpy + sklearn + statsmodels only). This module replaces the free-fit latent adjacency approach: composite trends are discovered as varimax-rotated factors of the differenced trend panel (identifiable, nameable, stable across seeds), and pairwise lead-lag edges are discovered only on the factor RESIDUALS (killing the common-driver false positives that dominate co-trending panels), directed by lag structure through stability-selected lasso VAR, and pruned by edge-level falsification.
Single entry point: discover_structure().
- autots.evaluator.tva.discovery.circular_shift_null(residuals: ndarray, screen_fn, n_reps: int = 20, seed: int = 42, quantile: float = 0.95) dict¶
How many edges does this screening procedure invent on coupling-free data?
Circularly shifting each series destroys cross-series timing while preserving its own autocorrelation and marginal distribution, so any edge still found is manufactured by the procedure rather than the data.
- Parameters:
screen_fn – callable (T, N) -> number of edges found.
- Returns:
dict with
counts,mean,quantileandfloor.
- autots.evaluator.tva.discovery.conditional_candidates(R: ndarray, top_k: int = 8, max_lag: int = 3) dict¶
Candidate neighbors per node: conditional dependence + lagged screening.
Contemporaneous screening uses GraphicalLassoCV when well-posed (Ledoit-Wolf + pinv fallback when N > T/2 or it fails to converge) — structurally what pairwise Granger cannot do: a candidate survives conditioning on all other series. Because a purely LAGGED parent can leave little contemporaneous partial correlation, the candidate set is unioned with the top-k series by |cross-correlation| over lags 1..max_lag. Stability-selected lasso downstream rejects the false ones.
- autots.evaluator.tva.discovery.deconfound(X: ndarray, scores: ndarray, factor_lags=(0, 1), shared: ndarray = None) ndarray¶
Regress each series on the factors at the given lags; return residuals.
Downstream edges then mean “B leads A after the shared macro force is removed.”
- Parameters:
shared – optional (T, M) extra nuisance columns (detected seasonality, holidays, shifts, anomalies), entered contemporaneously only – lagging a calendar effect would let it absorb real lead-lag signal.
- autots.evaluator.tva.discovery.discover_structure(trend_df: DataFrame, anchor_mask: ndarray | None = None, series_metadata: list | None = None, config: dict | None = None, seed: int = 42, extra_adjacencies: dict | None = None, external_factors: ndarray | None = None, components: dict | None = None) dict¶
Discover factors, loadings, and a conditional lead-lag edge table.
- Parameters:
trend_df – (T, N) wide DataFrame of decomposed trend components.
anchor_mask – optional (N,) bool; factor extraction uses anchors only (responders still get loadings via regression on the factors).
series_metadata – optional list of SeriesMetadata (or dicts) used for factor naming.
config – overrides for DEFAULT_DISCOVERY_CONFIG.
seed – RNG seed for block subsampling (topology is deterministic given seed).
extra_adjacencies – optional {family_name: (N, N) adjacency} for the ‘event’, ‘metadata’, ‘business’, and ‘causal’ families built elsewhere. Signed entries are honoured.
external_factors – optional (T, K) level-space factor paths to deconfound against instead of the internal difference-space SVD.
components – optional detector component dict (seasonality, holidays, level_shifts, anomalies), regressed out alongside the factors when
config['deconfound_components']is set.
- Returns:
dict with ‘factors’ (T, r) level-space composite tracks, ‘loadings’ (N, r) sparse signed, ‘factor_names’, ‘edges’ (list of dicts over real series names), ‘adjacency’ (N, N) merged dense form, ‘family_adjacencies’ {family: (N, N)}, ‘communities’ (N,), ‘config’ (resolved).
- autots.evaluator.tva.discovery.extract_factors(X: ndarray, config: dict) tuple¶
Truncated SVD + varimax + soft-thresholding on the differenced panel.
- Parameters:
X – (T-1, N) differenced, standardized panel.
config – resolved discovery config.
- Returns:
(factors_level (T, r), loadings (N, r), scores_diff (T-1, r))
- autots.evaluator.tva.discovery.falsify_edges(R: ndarray, edges: list, config: dict) list¶
Held-out one-step MSE with vs without each edge (zeroed coefficient).
Effect-level validation: an edge that does not reduce held-out error is pruned regardless of how significant it looked in-sample.
- autots.evaluator.tva.discovery.forecast_factors(factors: ndarray, horizon: int, phi: float = 0.9, fit_window: int = 90) ndarray¶
Damped local-linear forecast of the factor tracks.
Factors are few series with the full history each — a well-posed estimation problem — so a cheap damped-trend continuation is reliable.
- Parameters:
factors – (T, r) level-space factor tracks.
horizon – steps ahead.
phi – damping coefficient.
fit_window – trailing window used for the slope estimate.
- Returns:
(horizon, r) forecast deltas RELATIVE to the last factor value.
- autots.evaluator.tva.discovery.leadlag_edges(R: ndarray, config: dict) list¶
Windowed cross-correlation lead-lag candidates on trailing residuals.
Keep j -> i where the |cross-correlation| over positive lags of j peaks at lag > 0 and exceeds leadlag_min_corr; top leadlag_top_k per target. Cheap, fitting-free, recomputable at predict time.
- autots.evaluator.tva.discovery.match_factors(true_factors, est_factors) dict¶
Align estimated factors to known/true factors and score the recovery.
Both inputs are level-space tracks (discovery returns cumulative sums of the differenced factor scores; generator factors are levels), so they are differenced before comparison — the shared movement is what identifies a factor, not its arbitrary integration constant. Matching is a Hungarian assignment on the |correlation| matrix, which handles both permutation and sign indeterminacy of the factor basis.
- Parameters:
true_factors – (T, K) array-like (DataFrame ok) of reference factors.
est_factors – (T, r) array-like of estimated factors. May have a different number of factors than
true_factors.
- Returns:
‘assignment’: {true_index: est_index} for the matched pairs ‘correlations’: {true_index: signed correlation} per matched pair ‘signs’: {true_index: +1/-1} sign of each matched correlation ‘mean_abs_corr’: mean |corr| over matched pairs, averaged over
max(K, r)so missing/spurious factors are penalized’n_true’, ‘n_est’: factor counts
- Return type:
dict with
- autots.evaluator.tva.discovery.name_factors(loadings: ndarray, series_names: list, series_metadata: list | None) list¶
Name each factor by the modal shared metadata attribute of its top loaders.
(T, M) panel-mean nuisance columns from a detector component dict.
One column per available component family. Uses the panel mean, not the per-series value, so the per-series remainder stays visible to screening.
- autots.evaluator.tva.discovery.stability_selected_edges(R: ndarray, candidates: dict, config: dict, rng: Generator = None) list¶
Per-target stability-selected LassoLarsIC over lagged candidates.
Subsamples are contiguous windows (block length >> 2 * max_lag) because a plain iid bootstrap is wrong under serial dependence. The windows are a DETERMINISTIC rolling grid over the sample — seed-variance of the discovered topology is zero by construction. An edge (j -> i, lag) is kept when its selection frequency >= stability_threshold.
autots.evaluator.tva.factor_network module¶
Learned latent-factor trend mode (trend_network='factor').
- class autots.evaluator.tva.factor_network.LatentFactorTrend(*args, **kwargs)¶
Bases:
object
- autots.evaluator.tva.factor_network.estimate_factors_alternating(values: ndarray, n_factors: int, knot_spacing: int = 7, alpha: float = 0.001, iters: int = 6, init_window: int = 91, gls: bool = True, loading_l1: float = 0.0, loading_l1_adaptive: bool = True, loading_relax: bool = True)¶
Identify latent factor paths and loadings by alternating GLS/l1-TF.
Torch-free. This is the stage that actually finds the factors; the torch model refines around it.
loading_l1 > 0swaps every loading solve for the sparse solve in_fit_loadings()— including the final one, which would otherwise overwrite the sparse structure the loop just found.- Returns:
dict with ‘factors’ (T, K), ‘loadings’ (N, K), ‘coefs’ (P, K), ‘design’ (T, P), ‘weights’ (N,).
- autots.evaluator.tva.factor_network.fit_anchor_factor_model(*args, **kwargs)¶
- autots.evaluator.tva.factor_network.fit_latent_factor_model(*args, **kwargs)¶
- autots.evaluator.tva.factor_network.hinge_design(n_time: int, knot_spacing: int, max_knots: int = 200) ndarray¶
Trend-filtering basis:
[t/T, (t-k)_+/T]for knots everyspacing.B @ cis piecewise linear with breakpoints only wherecis nonzero, and is exactly zero att = 0— which is the model’s factor anchor.- Parameters:
n_time – number of time steps T.
knot_spacing – steps between candidate knots (>= 1).
max_knots – cap on candidate knots; widens spacing instead, since the l1 solve is superlinear in column count.
- Returns:
(T, P) design matrix, float32.
- autots.evaluator.tva.factor_network.hinge_knot_times(n_time: int, knot_spacing: int, max_knots: int = 200) ndarray¶
Time indices of the hinge knots
hinge_designwould build.Derived rather than stored so callers (e.g. the regime-slope continuation candidate) can’t drift out of sync with
hinge_design’s widening rule.
- autots.evaluator.tva.factor_network.identify_factors(values, n_factors, cfg, seed=42, device='cpu')¶
Identification dispatch: alternating estimator, optional sparse refit, rotation.
At the default config this is exactly
estimate_factors_alternating()followed by the optional C1 rotation, so the seam is a no-op until a knob moves. The sparse backends refine from the alternating fit and fall back to it, which is why they are safe to expose as a plain config value.- Parameters:
values – (T, N) normalized panel.
n_factors – fitted rank K.
cfg – a merged factor config (see
DEFAULT_FACTOR_CONFIG).seed – passed to the gradient tier.
device – passed to the gradient tier.
- Returns:
The identification contract dict.
- autots.evaluator.tva.factor_network.observed_mask(values: ndarray, mask=None) ndarray¶
(T, N) boolean mask of genuinely observed cells.
When
maskis None, derived from finiteness ofvalues— correct for a raw panel, degenerates to “everything observed” for a filled one (the back-compatible default).
- autots.evaluator.tva.factor_network.robust_level_scale(values: ndarray, floor_frac: float = 0.001)¶
Per-series robust center/scale in level space.
- Parameters:
values – (T, N) adjusted level panel.
floor_frac – floor on the scale as a fraction of the panel’s median scale, so near-constant series do not explode when normalized.
- Returns:
(center (N,), scale (N,)) float arrays.
- autots.evaluator.tva.factor_network.rotate_identification(ident: dict, method: str = 'varimax', kaiser: bool = True) dict¶
Re-express an identification result in a simple-structure basis.
Nothing upstream breaks the factor model’s rotational indeterminacy: the SVD initialization picks an arbitrary orthogonal basis and every loading solve is rotation-invariant lstsq, so the fit recovers the factor span but lands on a rotation of the true basis within it. Since generator (and, by assumption, real) loadings are simple-structure – each series predominantly loading one factor – rotating toward simple structure is the identifying restriction that was missing.
The reconstruction
factors @ loadings.Tis preserved exactly (to floating point), so this is a re-parameterization, not a refit: canonical correlation, subspace recovery and forecast accuracy are all invariant to it. Only basis-dependent quantities – which series loads which factor, and with what sign – change, which is precisely what the coherence graph reads.Three transforms are composed:
the rotation itself (
Rfromdiscovery._varimax(), or its promax extension), applied asL @ Rwith the counter-transformF @ inv(R).Tso the product is unchanged;re-normalization to unit-std factor increments, matching the convention
estimate_factors_alternating()exits on (a rotation mixes columns of differing scale, so this is not a no-op);a mass-vote sign orientation per factor – the same rule
coherence.resolve_signsuses – baked into the parameters, so the model itself is oriented rather than re-oriented at graph-build time.
- Parameters:
ident – the dict returned by
estimate_factors_alternating().method –
'varimax'|'quartimax'|'promax'.kaiser – row-normalize loadings before computing the rotation.
- Returns:
A new dict with rotated
factors,loadingsandcoefs(design/weightspassed through). Returnsidentunchanged on any numerical failure or unknown method – never raises, since a rotation failure must degrade to today’s behavior, not kill a fit.
- autots.evaluator.tva.factor_network.select_anchors(values: ndarray, mask=None, min_observed: int = 540)¶
Split a panel into long-history anchors and short-history responders.
Anchors are chosen purely from actual observed history — never from metadata, series names or declared launch dates, which are routinely wrong in production panels. Shared factor paths are then identified from anchors only, so a late-launching series can’t inject fabricated pre-launch rows into the common-trend estimate.
Callers should set
min_observedto roughly3 x forecast_horizon: a series needs enough history to identify its own loading and cover several non-overlapping validation windows before it defines the factor basis.- Returns:
(anchor_idx, responder_idx) — both ascending int arrays partitioning
range(N). If no column clears the threshold the longest-history columns are promoted instead, since a factor model needs anchors.
- autots.evaluator.tva.factor_network.select_n_factors(values: ndarray, cap: int = 6, window: int = 181) int¶
Rank hint from the smoothed level panel’s singular spectrum.
Daily noise dominates the raw spectrum, so it is smoothed first; rank is the Ahn-Horenstein eigenvalue-ratio argmax (
argmax_k s_k / s_{k+1}), which needs no threshold calibration.- Returns:
Integer in [1, cap].
- autots.evaluator.tva.factor_network.split_half_factor_stability(values: ndarray, n_factors: int, reference=None, n_reps: int = 3, seed: int = 42, **kwargs) ndarray¶
Per-factor version of
split_half_stability(). Returns (K,).The panel-level score answers “is there shared structure here at all”; this answers “which of these K columns is shared structure” — the question the coherence graph needs, since a single unreplicable column is enough to partition the panel wrongly while the panel-level score still looks fine.
Each replicate fits both disjoint halves, matches each half’s columns to the full-panel reference with
match_factors, and scores a factor by the weaker of its two matches: a column that only one half can find is not shared structure. Scores are averaged over replicates.- Parameters:
values – (T, N) panel, same input the full fit received.
n_factors – K, the reference rank.
reference – optional (T, K) full-panel factor paths. Fitted here when not supplied.
n_reps – split replicates.
seed – RNG seed for the splits.
**kwargs – forwarded to
estimate_factors_alternating().
- Returns:
(K,) array in [0, 1]; NaN entries where nothing could be scored. Never raises — a degenerate panel returns all-NaN.
- autots.evaluator.tva.factor_network.split_half_stability(values: ndarray, n_factors: int, n_reps: int = 3, seed: int = 42, **kwargs) float¶
Truth-free check that the factors are shared structure, not noise.
Real shared factors are recoverable from any subset of the series, so fitting two disjoint halves of the panel should yield matching paths. Factors that are merely absorbing idiosyncratic trends do not replicate.
- Returns:
Mean
match_factorsscore between the two halves, in [0, 1].
autots.evaluator.tva.fusion module¶
Digital Twin Fusion — Component Recombination.
Reconstructs full forecasts by combining trend network output with seasonality, holidays, level shifts, and anomalies. Different variants (attention-weighted or additive) serve as the final weaving mechanism.
autots.evaluator.tva.grouping module¶
Data-derived global / group factor structure.
Groups are found by consensus clustering (refit on random time/series subsets, cluster loadings, keep pairs stable across refits) rather than metadata, so a block only enters the model if it reproduces out of sample.
- autots.evaluator.tva.grouping.average_linkage_clusters(sim: ndarray, threshold: float) ndarray¶
Average-linkage agglomerative clustering on a similarity matrix.
Implemented directly rather than via scipy/sklearn so this module runs in minimal environments. Merging stops when no two clusters have average similarity above
threshold.- Parameters:
sim – (n, n) symmetric similarity matrix, larger = more similar.
threshold – minimum average similarity to merge.
- Returns:
(n,) integer cluster labels, densely numbered from 0.
- autots.evaluator.tva.grouping.discover_groups(values: ndarray, global_factors=None, config: dict = None, seed: int = 42, **fit_kwargs) dict¶
Find residual groups that survive a bootstrap reproduction test.
Projects the global factors out, then refits a small residual factor model on
refitsrandom time/series draws, clustering signed loading vectors each time (signed because two series moving opposite a common factor are a hedge pair, not the same group). A group is a set of series whose co-membership frequency across refits clearsthreshold.- Parameters:
values – (T, N) normalized panel.
global_factors – (T, Kg) already-fitted global factor paths, or None.
config – overrides for
DEFAULT_GROUPING_CONFIG.seed – RNG seed.
**fit_kwargs – forwarded to
estimate_factors_alternating.
- Returns:
dict with ‘labels’ (N,) group id per series (-1 = ungrouped), ‘groups’ {gid: [series indices]}, ‘co_membership’ (N, N) frequency, ‘n_refits’, and ‘residual’ (T, N) the panel groups were found in.
- autots.evaluator.tva.grouping.loading_graph(loadings: ndarray, labels=None, series_names=None, factor_names=None) dict¶
The series x factor loading matrix as the panel’s structural graph.
A factor-to-series bipartite graph (N x K) instead of series->series (N^2): exposes the structure actually fit rather than a separately-estimated adjacency.
- Parameters:
loadings – (N, K) signed loadings.
labels – optional (N,) group assignment (-1 for ungrouped).
series_names – optional list of series names.
factor_names – optional list of factor names.
- Returns:
dict with ‘loadings’, ‘group_assignment’, ‘series’, ‘factors’, ‘dominant_factor’ (argmax |loading| per series).
- autots.evaluator.tva.grouping.rolling_origin_score(values: ndarray, rank: int, horizon: int, n_origins: int = 3, membership=None, n_global: int = 0, **fit_kwargs) float¶
Held-out MASE of a rank-
rfactor model, torch-free.Each origin refits on truncated history and extrapolates via the same damped local-linear rule the trend model uses, so this is not an in-sample criterion. Rank 0 degenerates to a residual-only continuation.
- Parameters:
values – (T, N) normalized panel.
rank – number of factors (0 allowed).
horizon – forecast horizon per origin.
n_origins – non-overlapping origins, most recent first.
membership – optional (N,) group label array restricting a factor’s loadings to its own group’s series.
**fit_kwargs – forwarded to
estimate_factors_alternating.
- Returns:
Mean MASE over series and origins (lower is better); NaN if no origin has enough history.
- autots.evaluator.tva.grouping.select_rank(values: ndarray, candidates=(0, 1, 2, 3, 4, 6), horizon: int = 180, n_origins: int = 3, stability_threshold: float = 0.5, seed: int = 42, **fit_kwargs) dict¶
Choose a factor rank by held-out error and split-half stability.
In-sample fit is monotone in rank, so it can’t choose one, and held-out error alone is noisy on short panels. Rank is the minimum held-out MASE among ranks whose split-half factor agreement clears
stability_threshold; rank 0 is always admissible.- Parameters:
values – (T, N) normalized panel.
candidates – ranks to consider.
horizon – inner validation horizon.
n_origins – inner rolling origins.
stability_threshold – minimum split-half
match_factorsscore.seed – RNG seed for the split-half repetitions.
**fit_kwargs – forwarded to
estimate_factors_alternating.
- Returns:
dict with ‘rank’, ‘table’ [{‘rank’, ‘score’, ‘stability’, ‘admissible’}].
autots.evaluator.tva.losses module¶
TVA Loss Functions — The Allfather’s Judgment.
Six loss components that together enforce coherent, high-quality composite trends.
autots.evaluator.tva.metrics module¶
Shared scoring metrics for the TVA benchmark / validation harnesses.
Kept dependency-light (numpy + pandas only) so harnesses, notebooks and CI
gate scripts import the same definitions instead of drifting private copies.
All reductions are NaN-tolerant; degenerate inputs return np.nan rather
than raising, since these run inside sweep harnesses where one bad cell must
not abort the run.
Pairs of columns whose smoothed, differenced training trends correlate.
Derived from history alone (never the future), so the ground-set pairs are usable to score any model. Smoothing before differencing suppresses seasonal/noise so correlation reflects shared low-frequency drift. Capped at
max_pairsvia a seed-0 RNG subsample for reproducibility.
- autots.evaluator.tva.metrics.dca_error(forecast, actual, pairs) float¶
Mean |sign-agreement(forecast) - sign-agreement(actual)| over pairs.
Scored against the realised future rather than as a raw rate, since a model that forces lockstep is as wrong as one that scatters. Lower is better;
np.nanwhen there are no usable pairs.
- autots.evaluator.tva.metrics.direction_coherence(frame, pairs, window: int = 28) float¶
Fraction of
pairswhose net-change directions agree and are nonzero.The net-change analogue of
sign_agreement(). Zero-direction (flat) columns count toward the denominator but never toward the numerator, so “predict nothing moves” scores 0 rather than winning by abstention.np.nanwhen no pair is usable.
- autots.evaluator.tva.metrics.direction_coherence_error(forecast, actual, pairs, window: int = 28) float¶
|direction_coherence(forecast) - direction_coherence(actual)|.
Net-change analogue of
dca_error(): penalises both over- and under-coupled forecasts against the realised future. Lower is better.
- autots.evaluator.tva.metrics.directional_coherence(forecast, loadings) float¶
Fraction of same-dominant-factor, same-sign pairs forecast to agree.
Scored against the known factor structure since MASE alone won’t surface a mixed-direction forecast where everything should move together.
- Parameters:
forecast – DataFrame, horizon rows x series columns.
loadings – series (index) x factor (columns) loading matrix.
Returns
np.nanwhen loadings are empty or fewer than two series carry a loading – undefined, not zero, for a factorless negative control.
- autots.evaluator.tva.metrics.loading_structure_score(true_loadings, est_loadings, true_factors=None, est_factors=None, asserted_pairs=None) dict¶
Score how well estimated loadings recover the true loading structure.
Canonical correlation says whether the factor span was found; this says whether the basis within the span is the true one. A rotation of the correct span scores near chance here while scoring ~1.0 on span metrics, which is exactly the failure mode the coherence graph is built on top of.
- Parameters:
true_loadings – (N, K_true) generator loadings (DataFrame or array).
est_loadings – (N, K_est) fitted loadings, same series order.
true_factors – optional (T, K_true) true factor paths.
est_factors – optional (T, K_est) estimated factor paths. When both are supplied, columns are matched with
discovery.match_factors(Hungarian on differenced |corr|); otherwise a greedy |corr| match on the loading columns themselves is used.asserted_pairs – optional iterable of
(i, j)or(i, j, sign)series-index pairs the caller’s graph asserts as same-group (e.g.coherence._graph_pairs(group_graph(est_loadings, cfg), N)). Defaults to the pairs implied by the estimated loadings’ own (dominant factor, sign) partition.
- Returns:
dict with
pair_precision,pair_recall,pair_f1,n_pairs_asserted,n_pairs_true,dominant_recovery,dominant_recovery_matched,sign_agreement,matched_loading_corr,n_true,n_est. Undefined quantities arenp.nan(counts are0); never raises.
- autots.evaluator.tva.metrics.mase_value(actual, forecast, train, m: int = 7) float¶
Mean (over series) of MAE / in-sample seasonal-naive MAE.
Scaling by seasonal-naive error makes the number comparable across datasets of different magnitude; the
scale <= 1e-9guard drops constant series that would otherwise divide by ~zero and dominate.
- autots.evaluator.tva.metrics.net_change_direction(frame, window: int = 28) Series¶
Sign of (mean of last
windowrows - mean of firstwindowrows).Deliberately not last-point minus first-point: a single-point comparison is dominated by seasonality/noise and lets a near-flat forecast pick up a direction from an arbitrarily small difference.
Shrinks the window to
max(1, len(frame) // 2)when the frame has fewer than2 * windowrows, so the two windows never overlap.Returns a
pd.Seriesof -1.0 / 0.0 / 1.0 indexed by column (NaN for all-NaN columns).
- autots.evaluator.tva.metrics.oracle_normalized_coherence(forecast, actual, loadings) float¶
Coherence relative to the metric’s own ceiling on the real future.
A perfect forecast doesn’t score 1.0: the realised future only partly honours the factor structure once noise is added. Dividing by the actual future’s score stops gates from chasing an unreachable number.
Returns
np.nanwhen the oracle denominator is NaN or <= 0.
- autots.evaluator.tva.metrics.real_data_coherence(forecast, actual, pairs, window: int = 28) float¶
Recall of co-movement that actually happened.
Among pairs whose actual net-change directions agreed, the fraction the forecast also agreed on – conditional on truth, unlike
direction_coherence_error(), so it reads as a plain success rate on real panels with no factor loadings.Returns
np.nanwhen no actual pair agrees – an undefined denominator, not a score of zero.
- autots.evaluator.tva.metrics.sign_agreement(df, pairs) dict¶
Per-pair rate of first-difference sign agreement.
The raw ingredient of
dca_error(). Reported per pair (rather than averaged immediately) so callers can compare the same pair between a forecast and the actual future.
- autots.evaluator.tva.metrics.trend_only_coherence(trend_forecast, loadings) float¶
directional_coherence()applied to a trend-only forecast path.Isolates the structural component: seasonality/holidays can flip a first->last comparison on a short horizon. Named separately so gate tables and result JSONs record which path was scored.
autots.evaluator.tva.priors module¶
YggdrasilPriors — Prior specification and encoding for the TVA graph.
Acts as the ‘world tree’ for the forecasting graph, connecting related time series across different domains through shared metadata.
- class autots.evaluator.tva.priors.SeriesMetadata(name: str, attribute_values: Dict[str, str] | None = None, attribute_weights: Dict[str, float] | None = None, hierarchy_path: list | None = None, history_periods: int = 0, metric_type: str | None = None, surface: str | None = None, geography: str | None = None)¶
Bases:
objectMetadata descriptor for a single time series.
- name¶
Series identifier matching DataFrame column name.
- Type:
str
- attribute_values¶
Arbitrary categorical metadata for the series.
- Type:
Dict[str, str]
- hierarchy_path¶
Ordered path from root to leaf (e.g. [‘global’, ‘NA’, ‘US’]).
- Type:
list | None
- history_periods¶
Number of observed time periods available.
- Type:
int
- attribute_values: Dict[str, str]¶
- attribute_weights: Dict[str, float]¶
- property geography: str | None¶
- hierarchy_path: list | None¶
- history_periods: int = 0¶
- property metric_type: str | None¶
- name: str¶
- property surface: str | None¶
- class autots.evaluator.tva.priors.YggdrasilPriors(series_metadata: list = None, relationship_matrix: ndarray = None, prior_confidence: float = 0.3, detected_features: dict = None, trend_data: DataFrame = None, observed_history: Dict[str, int] = None, prior_construction_config: dict = None, causal_prior_construction_config: dict = None, series_names: list = None)¶
Bases:
objectConstructs prior adjacency matrices, metadata embeddings, and hierarchy matrices from series metadata. All methods return sensible defaults when metadata is absent, so TVA runs with or without priors.
- Parameters:
series_metadata – List of SeriesMetadata, one per series.
relationship_matrix – Optional (N, N) soft prior adjacency override.
prior_confidence – Weight of prior vs learned structure (0=ignore, 1=rigid).
- build_hierarchy_matrix() ndarray¶
Build summing matrix S for hierarchical reconciliation.
S has shape (L, M) where L = total nodes (aggregates + bottom), M = number of bottom-level series. Compatible with mint_reconcile(S, y_all, W).
Returns identity if no hierarchy paths are specified.
- build_metadata_embeddings() ndarray¶
Build (N, D_meta) one-hot embedding matrix from categorical metadata.
Encodes arbitrary categorical metadata keys as concatenated one-hot vectors. Returns zeros if no metadata is available.
- build_prior_adjacency() ndarray | None¶
Backward-compatible alias for structural prior construction.
- build_structural_prior_adjacency() ndarray | None¶
Construct a soft structural prior from explicit, metadata, and event sources.
- get_anchor_mask(min_history: int) ndarray¶
Return boolean mask (N,) where True = series has enough history to be an anchor.
Anchors shape the core latent trend graph. Responders inherit composite trends but do not perturb the core geometry.
- get_series_names() list¶
Return ordered list of series names.
- property n_series¶
- autots.evaluator.tva.priors.coerce_prior_adjacency(prior, series_names) ndarray | None¶
Normalize any supported user prior spec into a signed (N, N) matrix.
The single entry point for user-supplied graph priors, so every downstream consumer reads one shape regardless of how the prior was expressed.
Supported forms:
None->None(N, N)array-like -> validated and passed throughpd.DataFrame-> reindexed ontoseries_names(order-safe)list of
{'source', 'target', 'weight', 'directed'}dicts -> symmetric unless the row setsdirected: Truelist of name groups (
[['a', 'b'], ...]) -> each group is a clique at weight 1.0;{'series': [...], 'weight': 0.6}for a weaker group
Unknown series names raise a
RuntimeWarningnaming them and are dropped, never silently ignored.- Parameters:
prior – user-supplied prior in any of the forms above.
series_names – panel column order defining the matrix axes.
- Returns:
(N, N) float32, signed, clipped to [-1, 1], hollow diagonal; or None.
autots.evaluator.tva.reconciliation module¶
ReconciliationBridge — Bridge to AutoTS hierarchical reconciliation.
Thin adapter between TVA forecasts and the reconciliation functions in autots/tools/hierarchial.py (mint_reconcile, erm_reconcile, etc.).
- class autots.evaluator.tva.reconciliation.ReconciliationBridge(method: str = 'mint', covariance_method: str = 'ledoit_wolf')¶
Bases:
objectBridges TVA forecasts to hierarchical reconciliation methods.
Handles S matrix construction, covariance estimation, and method dispatch.
- Parameters:
method – Reconciliation method (‘mint’, ‘erm’, ‘iterative_mint’, ‘volatility_mint’). Default ‘mint’.
covariance_method – How to estimate W (‘ledoit_wolf’, ‘identity’, ‘sample’, or ‘structural’). ‘structural’ means the caller supplies W directly (see
reconcile’sWargument) rather than estimating it from a residual matrix — the case for TVA’s factor and torch-free modes, which have no per-node residual history but do produce a cross-series forecast covariance.
- reconcile(forecasts: DataFrame, S: ndarray, residuals: ndarray = None, W: ndarray = None) DataFrame¶
Apply hierarchical reconciliation to forecast DataFrame.
- Parameters:
forecasts – (T, L) DataFrame with all hierarchy levels. Columns should be ordered: aggregate nodes first, then bottom nodes, matching the row order of S.
S – (L, M) summing matrix from YggdrasilPriors.build_hierarchy_matrix().
residuals – (T_hist, L) historical residuals for covariance estimation. If None, uses identity covariance.
W – optional precomputed (L, L) covariance, used instead of estimating one from
residuals. Supplying it switches the covariance method to ‘structural’ for this call only, so the default residual-based path is untouched.
- Returns:
Reconciled DataFrame with same shape and columns as input.
- reconcile_with_volatility(forecasts: DataFrame, S: ndarray, residuals: ndarray, cov_bottom: ndarray = None, volatility_method: str = 'variance', volatility_power: float = 1.0, alpha: float = 0.5) DataFrame¶
Volatility-weighted reconciliation for covariance-aware adjustment.
- Parameters:
forecasts – (T, L) DataFrame.
S – (L, M) summing matrix.
residuals – (T_hist, L) historical residuals.
cov_bottom – (M, M) bottom-level covariance. Estimated if None.
volatility_method – ‘variance’, ‘std’, or ‘cv’.
volatility_power – Power to raise volatility weights to.
alpha – Blend between standard W and volatility weights.
- Returns:
Reconciled DataFrame.
autots.evaluator.tva.robust_input module¶
Robust trend-isolation input estimator for the latent-factor stage.
Alternative to TVA._build_adjusted_panel: jointly and robustly splits a
(T, N) level panel into shared low-rank trend, series-local trend, sparse
anomalies, and persistent intercept shifts, holding steps/spikes out of the
low-rank part so they can’t be absorbed into a spurious shared factor.
- autots.evaluator.tva.robust_input.compare_inputs(raw, detector_adjusted, robust_adjusted, oracle_trend) dict¶
Score four candidate factor-model inputs against the true trend.
Separates decomposition failure from factor-model failure: if the robust input scores near the oracle and the factor model still recovers poorly, the loss is downstream; if the robust input sits near the detector-adjusted panel, decomposition is the binding constraint.
Scores (all column-wise on mean-removed series, then averaged):
mean_abs_corr– mean |correlation| with oracle (1 is best);nrmse– RMSE / oracle std (0 is best, 1.0 = no better than flat);residual_energy_retained– non-trend energy left, as a fraction of the raw panel’s (raw scores 1.0, oracle 0.0, >1.0 means added noise).- Returns:
dict keyed by
'raw','detector','robust','oracle', each a dict of the three scores plus'n_scored', and a'ranking'key listing input names best-first bymean_abs_corr.
- autots.evaluator.tva.robust_input.robust_adjusted_panel(values, mask=None, components=None, shrink=None, config=None) dict¶
Jointly estimate a trend-only level panel from raw levels + nuisances.
Drop-in alternative to
TVA._build_adjusted_panel: treats detector components as nuisance regressors with adjustable trust (shrink) and alternates, to convergence, between an IRLS-Huber l1 trend fit per series, a truncated SVD splitting shared vs. series-local trend, soft-thresholded anomalies, and binary-segmented intercept shifts. Anomaly/shift terms are subtracted before the next pass’s trend fit so a step can’t be laundered into a shared “regime” via the low-rank factorization.Never raises on degenerate input; falls back to the raw panel minus the shrunk components (the pre-existing behaviour) on any failure.
- Parameters:
mask – (T, N) bool, True where observed; inferred from finiteness of
valueswhen None. A filled run must be False here, or it carries estimation weight it shouldn’t.components – dict of detector estimates, any subset of
('seasonality', 'holidays', 'anomalies', 'level_shifts').shrink – {component_name: float in [0, 1]} – how much of each component to subtract; < 1 hedges against a component estimate that absorbed real trend.
config – overrides of
DEFAULT_ROBUST_INPUT_CONFIG.
- Returns:
dict with
'adjusted','shared','idio','anomalies','shifts'(all (T, N) level units),'scale','center'(N,),'n_iters','converged','diagnostics'.
autots.evaluator.tva.safety module¶
Safety floor for the TVA factor trend mode.
trend_network='factor' extrapolates a whole-history latent trend, which is
prone to integrating a small slope error into a large level error at long
horizons. Four independent, optional corrections — blend toward
SeasonalNaive, cap the extrapolation, re-anchor the level, calibrate sigma by
horizon bucket — each selected by inner rolling-origin validation rather than
assumed.
Every identity path (blend weight 1.0, alpha 0.0, no cap, bucket_scales=None) is bitwise exact, so wiring this module in cannot perturb existing results until a selector actually fires. Nothing here raises; degenerate input returns the safe default. Pure numpy/pandas — no torch, no sklearn.
- autots.evaluator.tva.safety.apply_error_cap(forecast, lower, upper) ndarray¶
Clip a forecast into the band, elementwise.
NaN in a bound means “no cap on that cell”. With both bounds all-NaN the output is bitwise identical to the input.
- Parameters:
forecast – (H, N) forecast to clip.
lower – (H, N) lower bounds, NaN where uncapped.
upper – (H, N) upper bounds, NaN where uncapped.
- Returns:
(H, N) clipped forecast. Use
count_capped()to learn how many cells the band actually bound.
- autots.evaluator.tva.safety.apply_reanchor(forecast, offset, alpha) ndarray¶
Shift a forecast by
alpha * offset, one constant per series.- Parameters:
forecast – (H, N) forecast.
offset – scalar or (N,) level offset (
anchor - origin). Non-finite entries are treated as 0.0.alpha – scalar or (N,) selected fraction of the offset to apply.
- Returns:
(H, N) shifted forecast. With
alpha == 0the output is bitwise identical to the input.
- autots.evaluator.tva.safety.blend_forecasts(tva_fc, sn_fc, weights) ndarray¶
Convex combination
w * tva + (1 - w) * sn.- Parameters:
tva_fc – (H, N) factor-mode forecast.
sn_fc – (H, N) seasonal-naive forecast.
weights – scalar or (N,) weight on the TVA forecast, broadcast over the horizon. Non-finite weights are treated as 0.0.
- Returns:
(H, N) blended forecast.
weights == 1.0/0.0returntva_fc/sn_fcbitwise identical — callers rely on this.
- autots.evaluator.tva.safety.conformal_sigma(base_sigma, horizon: int, bucket_scales, config=None) ndarray¶
Expand a per-series sigma into a per-(step, series) sigma.
- Parameters:
base_sigma – scalar or (N,) per-series sigma from the model.
horizon –
bucket_scales – (H,) multipliers from
horizon_bucket_scales(), orNonefor the flat tile.config – unused today; accepted so callers can pass their config through uniformly with the rest of this module.
- Returns:
(H, N) sigma. With
bucket_scales=Nonethis is exactlynp.tile(base_sigma, (H, 1)), bitwise.
- autots.evaluator.tva.safety.count_capped(forecast, lower, upper) int¶
Number of cells
apply_error_cap()would move.Reported through
summarize()— a large fraction ofH * Nsignals structural divergence worth surfacing, not just silently correcting.
- autots.evaluator.tva.safety.error_cap_bounds(sn_fc, inner_abs_errors, horizon: int, config=None, bucket_scales=None) tuple¶
Band around SeasonalNaive that the final forecast may not leave.
The blend bounds the average damage; the cap bounds the worst cell. Band is
sn_fc ± error_cap_mult * q, whereqis theerror_cap_quantileof the baseline’s own absolute inner-validation error. Whenbucket_scalesis supplied the half-width is scaled per horizon step (1b’), since a flat band is too tight late and too loose early.- Parameters:
sn_fc – (H, N) seasonal-naive forecast, the band’s center.
inner_abs_errors – either a (N,) array of per-series quantiles already computed by the caller, or a list/array of (H, N) per-fold absolute error arrays, in which case the quantile is computed here.
horizon – H, the number of steps the bounds must cover.
config – overrides for
DEFAULT_SAFETY_CONFIG.bucket_scales – optional (H,) per-step multiplier from
horizon_bucket_scales().Nonegives a flat band.
- Returns:
(lower, upper), each (H, N). Cells whose quantile could not be estimated arenan, whichapply_error_cap()reads as “no cap”.
- autots.evaluator.tva.safety.horizon_bucket_scales(residuals_by_h, config=None, quantile=None) ndarray¶
How much wider the error distribution gets as the horizon grows.
Replaces a single sigma tiled flat across H, which under- and over-covers in opposite directions and averages out to look fine. Each bucket’s scale is the empirical absolute-residual quantile pooled over origins/steps/series, normalized so the first bucket is 1.0 — a pure shape that composes with any per-series sigma the caller has.
- Parameters:
residuals_by_h – (O, H, N) rolling-origin residuals (actual - forecast).
config – overrides for
DEFAULT_SAFETY_CONFIG;horizon_bucketsare inclusive 1-indexed step ranges.quantile – nominal level for the conformal scale. Defaults to the config’s
error_cap_quantile.
- Returns:
(H,) multiplier per horizon step. Steps past the last bucket take the last bucket’s scale; a bucket with no usable residuals inherits the previous bucket’s scale rather than collapsing to zero. Degenerate input returns all ones (flat, back-compatible).
- autots.evaluator.tva.safety.seasonal_naive_forecast(history, horizon: int, season_m: int = 7) ndarray¶
Tile the last season of observations forward.
The baseline the safety floor blends toward and caps around: no parameters to overfit and no slope to integrate, so error stays bounded by the seasonal pattern rather than growing with h. A missing seasonal slot falls back to the last finite value in that column (0.0 if none).
- Parameters:
history – (T, N) observed panel, most recent observation last.
horizon – number of steps to produce (H).
season_m – seasonal period, in steps. Values < 1 are treated as 1 (last-value persistence).
- Returns:
(H, N) float array. Empty history returns zeros.
- autots.evaluator.tva.safety.select_blend_weights(tva_folds, sn_folds, actual_folds, scale, config=None) ndarray¶
Pick, per series, how much weight the factor forecast has earned.
Scored on inner rolling-origin validation only, never on data the factor model was fit to or the outer test set. Ties favor the smaller TVA weight: equal measured accuracy is not equal risk, since the higher weight carries unmeasured extrapolation risk into the future.
- Parameters:
tva_folds – list of (H, N) factor forecasts, one per inner origin.
sn_folds – list of (H, N) seasonal-naive forecasts, same origins/shapes.
actual_folds – list of (H, N) realized values, same origins/shapes.
scale – (N,) MASE denominator (in-sample seasonal-naive MAE per series).
config – overrides for
DEFAULT_SAFETY_CONFIG.
- Returns:
(N,) weights on the TVA forecast, each drawn from
blend_grid. A series with no finite score anywhere — all-NaN history, no usable fold — gets 0.0, i.e. pure SeasonalNaive, the safe default.
- autots.evaluator.tva.safety.select_reanchor_alpha(tva_folds, actual_folds, anchor_levels, origin_levels, scale, config=None) ndarray¶
Decide how much of the origin level offset to remove, per series.
The whole-history factor reconstruction can sit at a level that disagrees with recent data — a constant bias worth removing over a long horizon. Not removed unconditionally: the recent median is also the noisiest level estimate available, so alpha is selected on inner folds with the same tie rule preferring alpha=0.0.
- Parameters:
tva_folds – list of F (H, N) factor forecasts at each inner origin.
actual_folds – list of F (H, N) realized values.
anchor_levels – (F, N) robust recent level — the median of the last
reanchor_windowobservations of the adjusted panel at each inner origin.origin_levels – (F, N) level the model’s own forecast implies at step 0 of each fold.
scale – (N,) MASE denominator.
config – overrides for
DEFAULT_SAFETY_CONFIG.
- Returns:
(N,) alpha per series, drawn from
reanchor_alphas. Series with no finite score, or with no usable offset, get 0.0 — no correction.
- autots.evaluator.tva.safety.summarize(blend_weights=None, reanchor_alphas=None, bucket_scales=None, capped_cells=None, scale=None, offsets=None, n_series=None, horizon=None, config=None, **extra) dict¶
JSON-safe diagnostics for
get_factor_diagnostics().Reports both aggregate summary stats and full per-series arrays.
n_series_zero_weight == Nis a valid outcome (factor mode earned nothing on this dataset), not a bug to tune away.- Parameters:
blend_weights – (N,) selected weights on the TVA forecast.
reanchor_alphas – (N,) selected re-anchor fractions.
bucket_scales – (H,) horizon-bucket sigma multipliers.
capped_cells – number of cells the error cap moved.
scale – (N,) MASE denominators used for selection.
offsets – (N,) applied level offsets.
n_series – N, if the caller wants it recorded explicitly.
horizon – H, if the caller wants it recorded explicitly.
config – the safety config in force; stored stringified-safe.
**extra – any additional scalars/arrays to record verbatim.
- Returns:
dict of json-serializable values; NaN becomes
None.
autots.evaluator.tva.scenario module¶
What-If Scenario Planning.
Two solvers for one problem — satisfy a user constraint while minimizing disruption to the rest of the forecast.
BifrostOptimizer freezes the trend network’s weights and optimizes a
latent perturbation by gradient descent. It needs a network, so it serves
trend_network='v1' and 'v2'.
ClosedFormScenario serves 'factor' and 'none', where there is
no network. Those forecasts are linear in a few factor paths, so the
minimum-disruption update is a Gaussian conditioning solve against the
forecast covariance — deterministic, torch-free, and cross-series aware
through the same Sigma that weights MinT reconciliation.
- class autots.evaluator.tva.scenario.BifrostOptimizer(tva_model, n_steps: int = 50, lr: float = 0.01)¶
Bases:
objectInference-time optimizer for scenario planning and what-if analysis.
Freezes all network weights. Creates an optimizable perturbation in latent space and runs gradient descent to satisfy user constraints while minimizing total perturbation. The perturbation penalty is covariance-weighted: high-variance components absorb more of the adjustment, protecting high-confidence elements.
- Parameters:
tva_model – Fitted TVA instance.
n_steps – Number of optimization steps.
lr – Learning rate for the perturbation optimizer.
- apply_constraint(series_name: str, timestep: int, target_value: float) DataFrame¶
Pin a specific series at a specific timestep to a target value.
- Parameters:
series_name – Column name of the series to constrain.
timestep – Forecast timestep index (0-based).
target_value – Desired value at that timestep.
- Returns:
Adjusted forecast DataFrame for ALL series.
- apply_growth_constraint(series_name: str, growth_rate: float) DataFrame¶
Constrain a series to a specific growth rate over the forecast horizon.
- Parameters:
series_name – Column name.
growth_rate – Desired growth rate (e.g. 0.05 for 5% growth).
- Returns:
Adjusted forecast DataFrame.
- apply_hierarchical_adjustment(level_name: str, target_value: float, hierarchy_matrix: ndarray = None) DataFrame¶
Set an aggregate level to target_value and propagate down proportionally.
This is an adjustment (set to exactly target_value at every timestep), not a constraint (threshold/clamp that only fires when exceeded).
The delta between target_value and the current aggregate sum is distributed to constituent bottom-level series proportional to their current share of that aggregate. When the current aggregate is near zero, the delta is split equally across constituent series.
- Parameters:
level_name – Name of the aggregate node to adjust, matched against the last component of hierarchy path tuples (e.g. ‘global’, ‘NA’, ‘EU’). If multiple nodes share the same last component, the first (shallowest) match is used.
target_value – Desired flat aggregate value applied across all forecast timesteps. The constituent series are scaled so their sum equals this value at every step.
hierarchy_matrix – (L, M) summing matrix S. If None, built automatically from priors configured at fit time. Required when no series metadata was provided.
- Returns:
Adjusted forecast DataFrame (same shape as predict()), with constituent series modified so their aggregate sum equals target_value.
- class autots.evaluator.tva.scenario.ClosedFormScenario(tva_model, covariance: ndarray = None)¶
Bases:
objectCovariance-weighted what-if solver for the factor and torch-free modes.
BifrostOptimizerbackpropagates a perturbation through the trend network, which is unavailable intrend_network='factor'and'none'(tva._network is Nonethere, so the optimizer dereferences None and raises). Those modes do not need backprop: their forecast is linear in a small set of factor paths, so any user constraint is a linear functional of the forecast vector and the minimum-disruption update has a closed form — the same Gaussian-conditioning / MinT-shaped solve:delta = Sigma A' (A Sigma A')^-1 (b - A y_hat)
This is the minimum-
Sigma-norm correction satisfyingA(y+delta) = bexactly, is deterministic, needs no optimizer steps, and reads the same covariance object as reconciliation — which is what makes the adjustment genuinely cross-series aware instead of a proportional top-down split.Non-factor components (seasonality, holidays, level shifts) need no special handling: they are additive offsets already inside the base forecast, so they enter only through the residual
b - A y_hat. Declared ratio identities are re-applied last, matching_predict_factor’s ordering.The solve happens in raw forecast units. When the factor model was fit in log space,
TVA.forecast_covariance()has already carried that geometry across with a delta-method Jacobian; solving in raw units keeps linear aggregate constraints (which a log-space solve would not preserve) exactly satisfied.- Parameters:
tva_model – Fitted TVA instance.
covariance – Optional precomputed (N, N) covariance. Fetched from
tva_model.forecast_covariance()when omitted.
- apply_constraint(series_name: str, timestep: int, target_value: float) DataFrame¶
Pin a series at a timestep; other series move along
Sigma.- Parameters:
series_name – Column name of the series to constrain.
timestep – Forecast timestep index (0-based).
target_value – Desired value at that timestep.
- Returns:
Adjusted forecast DataFrame for ALL series.
- apply_growth_constraint(series_name: str, growth_rate: float) DataFrame¶
Constrain a series to a growth rate over the forecast horizon.
- Parameters:
series_name – Column name.
growth_rate – Desired growth rate (e.g. 0.05 for 5% growth).
- Returns:
Adjusted forecast DataFrame.
- apply_hierarchical_adjustment(level_name: str, target_value: float, hierarchy_matrix: ndarray = None) DataFrame¶
Set an aggregate to
target_valueat every timestep.With a forecast covariance available the delta is distributed by the covariance-weighted solve — series that co-move with the rest of the aggregate absorb more of it than their raw share would suggest. Without one, this falls back to the proportional split
BifrostOptimizerperforms.- Parameters:
level_name – Aggregate node name, matched against the last component of hierarchy path tuples (e.g. ‘global’, ‘NA’) or the full slash-joined path. Shallowest match wins.
target_value – Desired flat aggregate value across all timesteps.
hierarchy_matrix – (L, M) summing matrix S. Built from the fitted priors when omitted.
- Returns:
Adjusted forecast DataFrame whose constituent series sum to
target_valueat every timestep.
- covariance(n_series: int = None)¶
(N, N) forecast covariance, or None when unavailable.
autots.evaluator.tva.seasonal module¶
Per-series seasonal-path arbitration.
Chooses per series, by held-out error, among: datepart (the panel-wide incumbent, kept on ties since only it expresses holiday/calendar structure), empirical (tile the series’ own recent detrended cycles), and amplitude (datepart path rescaled by a ridge-estimated per-series scalar).
- autots.evaluator.tva.seasonal.amplitude_scale(fitted: ndarray, residual: ndarray, config: dict = None) ndarray¶
(N,) per-series multiplier on the fitted seasonal path.
Ridge projection of recent detrended residual onto the fitted path, shrunk toward 1 (not 0) and clipped — a weak seasonal signal should keep the model’s estimate rather than collapse to no seasonality.
- autots.evaluator.tva.seasonal.assemble_choice(candidates: dict, choice: dict, default: str) ndarray¶
(H, N) seasonal path assembled column-by-column from a selection.
- autots.evaluator.tva.seasonal.empirical_profile(residual: ndarray, season_m: int, n_cycles: int = 2)¶
(season_m, N) average of the last
n_cyclesdetrended cycles.- Parameters:
residual – (T, N) panel with trend and level shifts already removed.
season_m – cycle length in steps.
n_cycles – how many trailing cycles to average.
- Returns:
(season_m, N) profile aligned so row 0 is the phase immediately following the end of
residual, or None when history is too short. Centered per series: a nonzero-mean cycle would double-count level, which the trend term already owns.
- autots.evaluator.tva.seasonal.select_seasonal_paths(candidates: dict, actual: ndarray, scale: ndarray = None, default: str = 'datepart', config: dict = None) dict¶
Choose a seasonal path per series from held-out error.
- Parameters:
candidates – {name: (H, N) full forecast for the held-out window}, each already summed with the same trend/holiday/shift terms so the only difference between them is the seasonal path.
actual – (H, N) realized values over that window.
scale – (N,) MASE denominator;
Nonescores in raw units.default – the incumbent’s name – kept unless beaten by more than the selection margin.
config – overrides for
DEFAULT_SEASONAL_CONFIG.
- Returns:
name}),
scores({name: (N,) scaled MAE}) andn_changed.- Return type:
dict with
choice({series_index
- autots.evaluator.tva.seasonal.tile_profile(profile: ndarray, horizon: int) ndarray¶
(H, N) forward tiling of a phase-aligned cycle profile.
autots.evaluator.tva.sparse_factor module¶
Sparse-code factor identification for trend_network='factor' (C9).
The alternating estimator in factor_network finds the factor span by
SVD and then solves loadings by regression. Both steps are rotation-invariant,
so the basis it lands on is arbitrary – and the basis is exactly what the
coherence graph consumes. Varimax (C1) addresses that by rotating afterwards,
and demonstrably works on a clean panel (dominant recovery 0.972), but a
post-hoc rotation needs an already-clean span: on the detector-adjusted panel
the C1-C8 ladder measured it moving dominant recovery 0.250 -> 0.139, i.e.
actively backwards.
This module takes the other route. Treat each series as a sample whose code
is its loading vector, parameterize the dictionary directly in the hinge trend
basis, and allow each code at most code_topk nonzeros. Sparsity is then
learned jointly with the span instead of rotated in afterwards, and a hard
support constraint is not rotation-invariant, so the basis is pinned without
any rotation step at all. Atoms nobody selects fall out, which makes the live
atom count an implicit rank estimate.
Two tiers share one objective:
'sparse_alt'Torch-free coordinate descent. Greedy support selection per series against an unpenalized idiosyncratic line, an unpenalized refit on the chosen support, then a dictionary update through the existing
factor_network._l1_trend_filter(). Support is re-selected from scratch every iteration, so nothing locks in.'sparse_ae'The same objective trained by Adam as an autoencoder over free codes. Signed magnitude-TopK, AuxK revival for dead atoms, dense warmup. Support does lock in after warmup, which is the substantive difference from the tier above and the reason both are worth measuring. Falls back to
'sparse_alt'when torch is unavailable.
Both are initialized from – and fall back to – the alternating estimator, so enabling one can never leave a panel worse identified than the default.
Returns the same identification contract the rest of the module speaks:
factors (T, K) centered with unit-std increments, loadings (N, K),
coefs (P, K) in the hinge basis, design (T, P), weights (N,), plus
idio_level/idio_slope and the atom diagnostics.
- autots.evaluator.tva.sparse_factor.identify(values, n_factors, init, method='sparse_alt', config=None, alpha=0.001, seed=42, device='cpu')¶
Refine an alternating identification into a sparse-code one.
- Parameters:
values – (T, N) normalized panel, as handed to the alternating estimator.
n_factors – fitted rank K.
init – the identification dict from
factor_network.estimate_factors_alternating(). Used as both the initializer and the fallback.method –
'sparse_alt'or'sparse_ae'.config – overrides for
DEFAULT_SPARSE_FACTOR_CONFIG.alpha – trend-filter smoothness, from the parent factor config.
seed – torch seed for the autoencoder tier.
device – torch device for the autoencoder tier.
- Returns:
An identification dict, or
Nonewhen the sparse fit failed or reconstructed materially worse thaninit– in which case the caller keepsinit. Never raises.
- autots.evaluator.tva.sparse_factor.signed_topk(z, k)¶
Keep the
klargest-magnitude entries of each row, with their signs.Non-negative activations are wrong for this problem:
coherencebuilds'f{k}+'/'f{k}-'group keys and a series can be negatively exposed to a factor, so the sign is load-bearing information rather than a nuisance to be rectified away.Selection is by exact rank, never by comparing against the k-th value: at initialization many entries are exactly zero and a
>=threshold would silently admit more thankof them.- Parameters:
z – (N, K) array of unconstrained codes.
k – number of nonzeros to keep per row.
- Returns:
(N, K) array, zero everywhere outside the selected support.
autots.evaluator.tva.structure module¶
Structure learning helpers for TVA.
This module keeps graph discovery, hierarchy discovery, export, and plotting separate from the forecast orchestration layer so new approaches can be swapped in without rewriting the TVA training loop.
- class autots.evaluator.tva.structure.GraphSnapshot(node_table: list, edge_table: list, adjacency_dense: ndarray, adjacency_thresholded: ndarray, assignment_matrices: list, topological_order: list, prior_adjacency: ndarray | None, is_acyclic: bool, cycle_score: float, series_table: list | None = None, prototype_table: list | None = None, affinity_table: list | None = None, prototype_edge_table: list | None = None)¶
Bases:
objectSerializable export of learned TVA structure.
- adjacency_dense: ndarray¶
- adjacency_thresholded: ndarray¶
- affinity_table: list | None = None¶
- assignment_matrices: list¶
- cycle_score: float¶
- edge_table: list¶
- is_acyclic: bool¶
- node_table: list¶
- prior_adjacency: ndarray | None¶
- prototype_edge_table: list | None = None¶
- prototype_table: list | None = None¶
- series_table: list | None = None¶
- to_dict() dict¶
- topological_order: list¶
- class autots.evaluator.tva.structure.StructureLearningConfig(enabled: bool = False, learn_hierarchy: bool = True, learn_dag: bool = True, max_levels: int = 3, pool_ratio: float = 0.5, min_nodes_per_level: int = 2, dag_penalty: float = 0.0, dag_warmup_epochs: float = 0.2, sparsity_weight: float = 0.01, assignment_entropy_weight: float = 0.01, assignment_full_rank_weight: float = 0.01, prior_tether_weight: float = 0.05, temporal_drift_weight: float = 0.0, threshold_for_export: float = 0.2)¶
Bases:
objectOpt-in configuration for TVA structure discovery.
- assignment_entropy_weight: float = 0.01¶
- assignment_full_rank_weight: float = 0.01¶
- dag_penalty: float = 0.0¶
- dag_warmup_epochs: float = 0.2¶
- derive_latent_sizes(n_anchor: int, fallback_sizes: list | None = None) list¶
Return deterministic latent widths from anchor count.
The returned list excludes the input anchor level and contains only derived latent widths ordered bottom-up.
- enabled: bool = False¶
- classmethod from_dict(config: dict | None = None) StructureLearningConfig¶
- learn_dag: bool = True¶
- learn_hierarchy: bool = True¶
- max_levels: int = 3¶
- min_nodes_per_level: int = 2¶
- pool_ratio: float = 0.5¶
- prior_tether_weight: float = 0.05¶
- sparsity_weight: float = 0.01¶
- structure_scale(epoch_index: int, total_epochs: int) float¶
- temporal_drift_weight: float = 0.0¶
- threshold_for_export: float = 0.2¶
- to_dict() dict¶
- warmup_start_epoch(total_epochs: int) int¶
- autots.evaluator.tva.structure.build_graph_snapshot(adjacency_dense: ndarray, assignment_matrices: list | None = None, threshold: float = 0.2, prior_adjacency: ndarray = None, anchor_names: list | None = None, full_series_names: list | None = None, anchor_mask: ndarray = None, series_metadata: list | None = None, prototype_weights: ndarray = None, prototype_forecasts: ndarray = None, global_prototype_weights: ndarray = None, decoded_top_trends: ndarray = None, dag_level: str = 'top') GraphSnapshot¶
Create a serializable snapshot from TVA structure state.
- Parameters:
dag_level – ‘top’ maps the dense adjacency onto the top latent level (legacy). ‘series’ maps it onto level 0 so the DAG is drawn over real series names (W-7).
- autots.evaluator.tva.structure.plot_graph_snapshot(snapshot: GraphSnapshot, view: str = 'dag', max_edges: int = 50, show_priors: bool = False, ax=None, metadata_color_by: str = 'auto')¶
Render a TVA structure snapshot with matplotlib.
- autots.evaluator.tva.structure.topological_order_from_adjacency(adjacency: ndarray) list¶
Return a node ordering if the graph is acyclic, otherwise [].
autots.evaluator.tva.trend_network module¶
Composite Trend Network — The Sacred Timeline.
V1: Hierarchical Latent Trend Graph (deterministic structure). V2: Learned Directed Composite Graph (learned adjacency, adaptive prototypes).
The trend network operates only on isolated trend components, not raw series. It compresses per-series trends into a small set of shared composite prototypes and reconstructs per-series trend forecasts that are structurally consistent with those shared states.
autots.evaluator.tva.tva module¶
TVA — Time Variant Architecture.
Top-level orchestrator for the TVA forecasting graph. Ties together decomposition, priors, trend network, fusion, losses, reconciliation, and scenario planning into a single fit/predict interface.
Note a wrapper exists in /autots/models/tva_model.py for the AutoTS search integration.
Some reference papers: https://www.mdpi.com/2227-7390/13/20/3288 https://openreview.net/pdf?id=GYSG2vF6z5 https://arxiv.org/html/2409.10996v2 https://arxiv.org/html/2507.15119v2#S4 https://www.researchgate.net/profile/Jawad-Chowdhury-6/publication/379087074_CD-_NOTEAR[…]N0UGFnZSI6InB1YmxpY2F0aW9uIiwicGFnZSI6InB1YmxpY2F0aW9uIn19 https://openreview.net/pdf?id=80g3Yqlo1a https://openreview.net/pdf?id=WjDjem8mWE
- class autots.evaluator.tva.tva.TVA(detector_params: dict = None, trend_network: str = 'v2', fusion: str = 'attention', series_metadata: list = None, prior_adjacency: ndarray = None, prior_confidence: float = 0.3, causal_prior: ndarray = None, prior_construction_config: dict = None, causal_prior_construction_config: dict = None, discovery_config: dict = None, d_token: int = 32, n_meso: int | str = 'auto', n_global: int | str = 'auto', n_prototypes: int | str = 'auto', n_heads: int = 2, epochs: int = 200, lr: float = 0.001, batch_size: int = 32, window_size: int = 91, forecast_horizon: int = 28, recency_halflife_days: float | None = None, loss_weights: dict = None, reconciliation_method: str = None, min_anchor_history: int = 180, holiday_country=None, holiday_countries: dict = None, device: str = None, random_seed: int = 42, verbose: int = 1, prototype_assignment_method: str = 'cosine', prototype_assignment_temperature: float = 1.0, structure_learning_config: dict = None, n_factors: int | str = 'auto', factor_knot_spacing: int = 7, factor_max_lag: int = 0, factor_config: dict = None, coherence_config: dict = None, derived_definitions: dict = None, factor_deconfound_edges: bool = False, reconciliation_covariance: str = 'auto')¶
Bases:
objectTime Variant Architecture — end-to-end coherent forecasting graph.
The Common Operating Picture for time series. Produces structurally consistent forecasts across related series by routing all trends through shared composite prototypes. For all Time. Always.
- Parameters:
detector_params – Dict passed to TimeSeriesFeatureDetector.
trend_network – ‘v2’ (learned directed, default), ‘v1’ (hierarchical latent), ‘factor’ (learned latent-factor trend), or ‘none’ with a damped rolling-trend extrapolation while still running factor/edge discovery, MinT reconciliation, and residual-sigma intervals. ‘none’ is the Phase-4 kill-rule configuration: fast, interpretable, and a strong baseline the network must beat.
fusion –
How stochastic components (trend, seasonality, holidays) are recombined before level shifts are added additively. ‘attention’ (default): DigitalTwinFusion — self-attention contextualizes
component embeddings; sigmoid gates independently fade each component in/out (gate in [0,1]) applied to the original values.
- ’direct’: DirectAttentionFusion — self-attention contextualizes component
embeddings; attended representations are projected directly to scalar contributions summed as a residual over the originals. More purely attention-driven; zero-init ensures pure-additive start.
’additive’: AdditiveFusion — plain sum, no learned parameters.
series_metadata – List of SeriesMetadata for prior construction.
prior_adjacency – Optional co-movement graph prior. Accepts an (N, N) matrix, a labelled DataFrame, a list of
{'source', 'target', 'weight', 'directed'}edge dicts, or a list of name groups (each group a clique) – seepriors.coerce_prior_adjacency. Signed: a negative weight is a substitution claim and pushes the pair apart.prior_confidence – Weight of priors (0=ignore, 1=rigid). Sets the merge weight of the ‘business’/’causal’ edge families (against 1.0 for the data-derived ones) and the coherence blend weight.
causal_prior – Optional directed prior in the same forms, used for V2 adjacency regularization and kept as its own edge family.
prior_construction_config – Dict configuring automatic structural prior construction from event clusters and metadata. Defaults to blending detected changepoints/anomalies with metadata similarity ({‘sources’: [‘event’, ‘metadata’], …}). Pass {} to disable.
causal_prior_construction_config – Deprecated and ignored — series-level causal structure now comes from factor-residual discovery (autots.evaluator.tva.discovery). Use discovery_config instead.
discovery_config – Dict overriding discovery.DEFAULT_DISCOVERY_CONFIG (factor extraction, conditional screening, stability selection, lead-lag scan, falsification). Pass {‘enabled’: False} to skip structure discovery entirely.
d_token – Token/latent dimension.
n_meso – Number of meso latent nodes, or ‘auto’ (default) to set as 2 * n_global derived from N series.
n_global – Number of global latent nodes, or ‘auto’ (default) to set as max(2, ceil(sqrt(N_anchors))). Controls the DAG size.
n_prototypes – Number of prototype trend signatures, or ‘auto’ (default) to set as max(2, round(log2(N_anchors + 1))). Capped at 8.
n_heads – Attention heads.
epochs – Maximum training epochs. Default 200; training normally stops earlier via time-ordered validation early stopping (patience 8).
lr – Learning rate.
batch_size – Training batch size.
window_size – Input trend window length.
forecast_horizon – Output forecast length.
loss_weights – Dict overriding loss component weights.
reconciliation_method – None, ‘mint’, ‘erm’, etc.
reconciliation_covariance – how MinT’s W is obtained when no per-node residual matrix is available (the ‘factor’ and ‘none’ modes, where the network-based residual estimator returns None and W therefore falls back to the identity). ‘auto’ (default) follows the module constant
RECONCILIATION_COVARIANCE_AUTO, ‘structural’ usesS @ forecast_covariance() @ S.T, ‘identity’ forces the previous behaviour.min_anchor_history – Minimum periods for a series to be an anchor.
device – ‘cpu’, ‘cuda’, or None (auto-detects cuda if available, else cpu).
random_seed – Reproducibility seed.
verbose – 0=silent, 1=progress bar, 2=per-epoch loss.
prototype_assignment_method – Prototype assignment method for bottleneck (‘cosine’, ‘l2’, ‘linear’). Defaults to ‘cosine’.
prototype_assignment_temperature – Temperature for prototype assignment logits.
n_factors – Number of latent factors for trend_network=’factor’, or ‘auto’ (default) to pick a rank from the smoothed level-panel singular spectrum.
factor_knot_spacing – Candidate-knot spacing (steps) of the factor trend-filter basis in ‘factor’ mode.
factor_max_lag – Maximum learned per-series response lag in ‘factor’ mode. Defaults to 0 (contemporaneous)
factor_config – Dict overriding factor_network.DEFAULT_FACTOR_CONFIG.
coherence_config – Post-forecast coherence-shrink settings. Merged over
factor_config['coherence_config']; supplying it also enables the shrink unlessfactor_configset ‘coherence’ explicitly. None (default) leaves the shrink off.derived_definitions – Declared ratio identities {column: (num, den)}; never inferred from column names.
factor_deconfound_edges – Re-run series edge discovery deconfounded against the learned factors (‘factor’ mode). Off by default
structure_learning_config – Dict enabling DAG and dynamic hierarchy learning in the V2 trend network.
- fit(df: DataFrame) TVA¶
Full TVA pipeline: decompose, build priors, train network.
- Parameters:
df – Wide DataFrame with DatetimeIndex and numeric columns.
- Returns:
self
- forecast_covariance(horizon: int = None)¶
(N, N) raw-unit cross-series forecast-error covariance, or None.
One covariance object for the callers that each otherwise invent their own weighting: MinT’s
W, the closed-form what-if solver, and (later) interval coherence. It is built from the rolling-origin residual matrix the trend model already forms, blended toward a low-rankLambda Sigma_f Lambda' + diag(psi)target, and floored at the same per-series sigma the shipped prediction intervals use.Only the modes whose trend model is linear in a small set of factors are served:
trend_network='factor'(loadings from the fitted factor model) and'none'(loadings from structure discovery, residuals from rolling the damped-trend rule).'v1'/'v2'return None — those already have_compute_recent_reconciliation_residuals()for reconciliation and the backprop scenario solver for what-if, and a covariance built from a different model than the one forecasting would misdescribe them.- Parameters:
horizon – forecast horizon the covariance should describe. Defaults to
forecast_horizon.- Returns:
(sigma, info)withsigmaan (N, N) covariance in the raw units of the input data andinfoa diagnostics dict carryingalpha(shrinkage intensity),beta(structural scale),psi,floor_bindingandn_samples. ReturnsNonewhen unfitted, when the mode is not served, or when no usable residual matrix or loadings exist — callers degrade to their previous behaviour rather than seeing an exception.
- get_composite_trends() dict¶
Return learned composite/prototype trends for inspection.
- Returns:
Dict with ‘prototypes’ (K, D), ‘composite_trend’ (n_global, T_forecast), and ‘prototype_weights’ (N, K).
- get_edges() DataFrame¶
Return the discovered edge table over real series names (W-7).
These are predictive screening results, not causal claims. An edge means “after removing the shared factors, this series’ history helped predict that one’s, stably across subsamples” — useful for screening, not evidence of mechanism. Series edges stay disabled for forecasting until they clear the false-positive/lag-recovery/follower-forecast gates together; the trustworthy structural output today is the factor -> series loading graph (
get_factor_graph/get_factor_diagnostics’sloading_graph).Columns: source, target, lag, sign, weight, family, stability, delta_mse. Empty DataFrame when discovery was disabled or found nothing. In trend_network=’factor’ mode the induced factor -> series bipartite edges (family ‘factor’) are appended; see get_factor_graph.
- get_factor_diagnostics() dict¶
Everything the factor mode decided, in one JSON-safe dict.
- Returns:
dict with the selected rank, factors/loadings, anchor/responder masks, stability scores, continuation choice, per-series blend weights, re-anchor alphas, gated series (by reason), the learned loading graph, validation scores and the coherence adjustment magnitude. Keys are absent rather than fabricated when the corresponding mechanism was not enabled.
- get_factor_graph() DataFrame¶
Induced factor -> series bipartite graph (‘factor’ mode).
One row per (factor, series) pair with a non-negligible loading, giving the signed loading weight and the series’ learned response lag. This is the structure the latent-factor mode actually uses to forecast, as opposed to the series->series edges from discovery.
- Returns:
DataFrame with columns source, target, lag, sign, weight, family. Empty when not in ‘factor’ mode or not fitted.
- get_factors() dict¶
Return named composite factors and their sparse signed loadings (W-7).
- Returns:
Dict with ‘factors’ (DataFrame, one named column per factor, level space, indexed like the training data), ‘loadings’ (DataFrame, series x factor), and ‘factor_names’. In trend_network=’factor’ mode these are the learned level-space paths, plus ‘lags’, ‘variance_share’, ‘phi’ and ‘diag’.
- get_graph() ndarray¶
Return the adjacency matrix (learned for V2, prior for V1).
- Returns:
(M, M) numpy array.
- get_graph_snapshot(threshold: float = None, include_priors: bool = True) dict¶
Return a serializable snapshot of the learned graph and hierarchy.
- plot_graph(view: str = 'dag', threshold: float = None, max_edges: int = 50, show_priors: bool = False, ax=None, metadata_color_by: str = 'auto')¶
Plot the learned graph, hierarchy, or adjacency heatmap.
- predict(forecast_length: int = None) DataFrame¶
Generate forecasts for all series.
- Parameters:
forecast_length – Number of future periods. Defaults to forecast_horizon.
- Returns:
Wide DataFrame (forecast_length, N) with forecasted values.
- reconcile(forecasts: DataFrame = None, residuals: ndarray = None, aggregate_sigma=None) DataFrame¶
Apply hierarchical reconciliation if configured.
Note
When
forecastsholds only the bottom level, this method builds the aggregate rows by summing it through S — which places the input exactly in the coherent subspace MinT projects onto, so reconciliation returns it unchanged for any W, in any trend mode. Reconciliation can only move a number when the aggregate levels carry a forecast that was not derived from the bottom one; pass a full (L-column)forecastsframe, ordered aggregates-then-bottom, to supply one.- Parameters:
forecasts – Forecast DataFrame. If None, generates fresh predictions. Bottom-level columns only, or all L hierarchy levels with the aggregate columns first (see the note above).
residuals – Optional historical residual matrix for all hierarchy levels. If omitted, TVA estimates recent one-step residuals from the last 30-90 training windows so covariance-aware reconciliation methods can use a meaningful W matrix.
aggregate_sigma – Optional (n_agg,) forecast-error standard deviations for independently-produced aggregate forecasts. Only consulted on the structural-covariance path, where it is what stops Sigma from cancelling out of MinT’s estimator (see
_structural_reconciliation_W()).
- Returns:
Reconciled DataFrame.
- what_if(**constraints) DataFrame¶
Scenario planning: adjust the forecast to satisfy user constraints.
Two solvers behind one signature, picked by what the fit produced.
trend_network='v1'/'v2'backpropagate a latent perturbation through the trend network (BifrostOptimizer).'factor'and'none'have no network to backprop through — the optimizer used to dereferenceself._networkand raise there — but their forecast is linear in the factor paths, soClosedFormScenariosolves the same minimum-disruption problem in closed form underforecast_covariance().- Parameters:
**constraints – Passed to the solver’s methods. Supported keys: - series_name, timestep, target_value -> apply_constraint - series_name, growth_rate -> apply_growth_constraint - level_name, target_value -> apply_hierarchical_constraint
- Returns:
Adjusted forecast DataFrame.
Module contents¶
TVA — Time-varying Vectorized Architecture.
A modular forecasting graph that produces structurally consistent forecasts across related time series via shared composite trend prototypes.
- class autots.evaluator.tva.GraphSnapshot(node_table: list, edge_table: list, adjacency_dense: ndarray, adjacency_thresholded: ndarray, assignment_matrices: list, topological_order: list, prior_adjacency: ndarray | None, is_acyclic: bool, cycle_score: float, series_table: list | None = None, prototype_table: list | None = None, affinity_table: list | None = None, prototype_edge_table: list | None = None)¶
Bases:
objectSerializable export of learned TVA structure.
- adjacency_dense: ndarray¶
- adjacency_thresholded: ndarray¶
- affinity_table: list | None = None¶
- assignment_matrices: list¶
- cycle_score: float¶
- edge_table: list¶
- is_acyclic: bool¶
- node_table: list¶
- prior_adjacency: ndarray | None¶
- prototype_edge_table: list | None = None¶
- prototype_table: list | None = None¶
- series_table: list | None = None¶
- to_dict() dict¶
- topological_order: list¶
- class autots.evaluator.tva.NornDecomposer(detector_params: dict = None, holiday_country=None, holiday_countries: dict = None)¶
Bases:
objectWraps TimeSeriesFeatureDetector to extract structured component DataFrames.
Produces trend, seasonality, holidays, level_shifts, anomalies, and noise as aligned DataFrames sharing the same index and columns as the input.
- Parameters:
detector_params – Dict of kwargs passed to TimeSeriesFeatureDetector.__init__.
- fit(df: DataFrame) NornDecomposer¶
Fit the feature detector on a wide DataFrame.
- Parameters:
df – Wide DataFrame with DatetimeIndex and one numeric column per series.
- Returns:
self
- get_components() dict¶
Extract decomposition components as DataFrames.
- Returns:
Dict with keys ‘trend’, ‘seasonality’, ‘holidays’, ‘level_shifts’, ‘anomalies’, ‘noise’. Each value is a DataFrame (T, N) aligned with the original input index and columns.
- get_features() dict¶
Return the full detected features dict from the underlying detector.
Includes changepoints, holiday impacts, anomaly records, etc. Useful for constructing priors or inspecting detection results.
- get_forecast_components(forecast_length: int) dict¶
Forward-project seasonality, holidays, level shifts, and trend.
Uses the detector’s built-in forecast method to project known deterministic components into the future.
- Parameters:
forecast_length – Number of future periods to project.
- Returns:
Dict with same keys as get_components(), each a DataFrame of shape (forecast_length, N).
- get_residual_sigma()¶
Per-series one-step residual sigma from the underlying detector.
Exposes the detector’s own reconstruction-residual sigma (the same quantity its OLS prediction intervals are built from) so TVA can add it in quadrature to the network’s learned sigma. Returns an (N,) float array aligned to the fitted columns, or None when unavailable.
- class autots.evaluator.tva.SeriesMetadata(name: str, attribute_values: Dict[str, str] | None = None, attribute_weights: Dict[str, float] | None = None, hierarchy_path: list | None = None, history_periods: int = 0, metric_type: str | None = None, surface: str | None = None, geography: str | None = None)¶
Bases:
objectMetadata descriptor for a single time series.
- name¶
Series identifier matching DataFrame column name.
- Type:
str
- attribute_values¶
Arbitrary categorical metadata for the series.
- Type:
Dict[str, str]
- hierarchy_path¶
Ordered path from root to leaf (e.g. [‘global’, ‘NA’, ‘US’]).
- Type:
list | None
- history_periods¶
Number of observed time periods available.
- Type:
int
- attribute_values: Dict[str, str]¶
- attribute_weights: Dict[str, float]¶
- property geography: str | None¶
- hierarchy_path: list | None¶
- history_periods: int = 0¶
- property metric_type: str | None¶
- name: str¶
- property surface: str | None¶
- class autots.evaluator.tva.StructureLearningConfig(enabled: bool = False, learn_hierarchy: bool = True, learn_dag: bool = True, max_levels: int = 3, pool_ratio: float = 0.5, min_nodes_per_level: int = 2, dag_penalty: float = 0.0, dag_warmup_epochs: float = 0.2, sparsity_weight: float = 0.01, assignment_entropy_weight: float = 0.01, assignment_full_rank_weight: float = 0.01, prior_tether_weight: float = 0.05, temporal_drift_weight: float = 0.0, threshold_for_export: float = 0.2)¶
Bases:
objectOpt-in configuration for TVA structure discovery.
- assignment_entropy_weight: float = 0.01¶
- assignment_full_rank_weight: float = 0.01¶
- dag_penalty: float = 0.0¶
- dag_warmup_epochs: float = 0.2¶
- derive_latent_sizes(n_anchor: int, fallback_sizes: list | None = None) list¶
Return deterministic latent widths from anchor count.
The returned list excludes the input anchor level and contains only derived latent widths ordered bottom-up.
- enabled: bool = False¶
- classmethod from_dict(config: dict | None = None) StructureLearningConfig¶
- learn_dag: bool = True¶
- learn_hierarchy: bool = True¶
- max_levels: int = 3¶
- min_nodes_per_level: int = 2¶
- pool_ratio: float = 0.5¶
- prior_tether_weight: float = 0.05¶
- sparsity_weight: float = 0.01¶
- structure_scale(epoch_index: int, total_epochs: int) float¶
- temporal_drift_weight: float = 0.0¶
- threshold_for_export: float = 0.2¶
- to_dict() dict¶
- warmup_start_epoch(total_epochs: int) int¶
- class autots.evaluator.tva.TVA(detector_params: dict = None, trend_network: str = 'v2', fusion: str = 'attention', series_metadata: list = None, prior_adjacency: ndarray = None, prior_confidence: float = 0.3, causal_prior: ndarray = None, prior_construction_config: dict = None, causal_prior_construction_config: dict = None, discovery_config: dict = None, d_token: int = 32, n_meso: int | str = 'auto', n_global: int | str = 'auto', n_prototypes: int | str = 'auto', n_heads: int = 2, epochs: int = 200, lr: float = 0.001, batch_size: int = 32, window_size: int = 91, forecast_horizon: int = 28, recency_halflife_days: float | None = None, loss_weights: dict = None, reconciliation_method: str = None, min_anchor_history: int = 180, holiday_country=None, holiday_countries: dict = None, device: str = None, random_seed: int = 42, verbose: int = 1, prototype_assignment_method: str = 'cosine', prototype_assignment_temperature: float = 1.0, structure_learning_config: dict = None, n_factors: int | str = 'auto', factor_knot_spacing: int = 7, factor_max_lag: int = 0, factor_config: dict = None, coherence_config: dict = None, derived_definitions: dict = None, factor_deconfound_edges: bool = False, reconciliation_covariance: str = 'auto')¶
Bases:
objectTime Variant Architecture — end-to-end coherent forecasting graph.
The Common Operating Picture for time series. Produces structurally consistent forecasts across related series by routing all trends through shared composite prototypes. For all Time. Always.
- Parameters:
detector_params – Dict passed to TimeSeriesFeatureDetector.
trend_network – ‘v2’ (learned directed, default), ‘v1’ (hierarchical latent), ‘factor’ (learned latent-factor trend), or ‘none’ with a damped rolling-trend extrapolation while still running factor/edge discovery, MinT reconciliation, and residual-sigma intervals. ‘none’ is the Phase-4 kill-rule configuration: fast, interpretable, and a strong baseline the network must beat.
fusion –
How stochastic components (trend, seasonality, holidays) are recombined before level shifts are added additively. ‘attention’ (default): DigitalTwinFusion — self-attention contextualizes
component embeddings; sigmoid gates independently fade each component in/out (gate in [0,1]) applied to the original values.
- ’direct’: DirectAttentionFusion — self-attention contextualizes component
embeddings; attended representations are projected directly to scalar contributions summed as a residual over the originals. More purely attention-driven; zero-init ensures pure-additive start.
’additive’: AdditiveFusion — plain sum, no learned parameters.
series_metadata – List of SeriesMetadata for prior construction.
prior_adjacency – Optional co-movement graph prior. Accepts an (N, N) matrix, a labelled DataFrame, a list of
{'source', 'target', 'weight', 'directed'}edge dicts, or a list of name groups (each group a clique) – seepriors.coerce_prior_adjacency. Signed: a negative weight is a substitution claim and pushes the pair apart.prior_confidence – Weight of priors (0=ignore, 1=rigid). Sets the merge weight of the ‘business’/’causal’ edge families (against 1.0 for the data-derived ones) and the coherence blend weight.
causal_prior – Optional directed prior in the same forms, used for V2 adjacency regularization and kept as its own edge family.
prior_construction_config – Dict configuring automatic structural prior construction from event clusters and metadata. Defaults to blending detected changepoints/anomalies with metadata similarity ({‘sources’: [‘event’, ‘metadata’], …}). Pass {} to disable.
causal_prior_construction_config – Deprecated and ignored — series-level causal structure now comes from factor-residual discovery (autots.evaluator.tva.discovery). Use discovery_config instead.
discovery_config – Dict overriding discovery.DEFAULT_DISCOVERY_CONFIG (factor extraction, conditional screening, stability selection, lead-lag scan, falsification). Pass {‘enabled’: False} to skip structure discovery entirely.
d_token – Token/latent dimension.
n_meso – Number of meso latent nodes, or ‘auto’ (default) to set as 2 * n_global derived from N series.
n_global – Number of global latent nodes, or ‘auto’ (default) to set as max(2, ceil(sqrt(N_anchors))). Controls the DAG size.
n_prototypes – Number of prototype trend signatures, or ‘auto’ (default) to set as max(2, round(log2(N_anchors + 1))). Capped at 8.
n_heads – Attention heads.
epochs – Maximum training epochs. Default 200; training normally stops earlier via time-ordered validation early stopping (patience 8).
lr – Learning rate.
batch_size – Training batch size.
window_size – Input trend window length.
forecast_horizon – Output forecast length.
loss_weights – Dict overriding loss component weights.
reconciliation_method – None, ‘mint’, ‘erm’, etc.
reconciliation_covariance – how MinT’s W is obtained when no per-node residual matrix is available (the ‘factor’ and ‘none’ modes, where the network-based residual estimator returns None and W therefore falls back to the identity). ‘auto’ (default) follows the module constant
RECONCILIATION_COVARIANCE_AUTO, ‘structural’ usesS @ forecast_covariance() @ S.T, ‘identity’ forces the previous behaviour.min_anchor_history – Minimum periods for a series to be an anchor.
device – ‘cpu’, ‘cuda’, or None (auto-detects cuda if available, else cpu).
random_seed – Reproducibility seed.
verbose – 0=silent, 1=progress bar, 2=per-epoch loss.
prototype_assignment_method – Prototype assignment method for bottleneck (‘cosine’, ‘l2’, ‘linear’). Defaults to ‘cosine’.
prototype_assignment_temperature – Temperature for prototype assignment logits.
n_factors – Number of latent factors for trend_network=’factor’, or ‘auto’ (default) to pick a rank from the smoothed level-panel singular spectrum.
factor_knot_spacing – Candidate-knot spacing (steps) of the factor trend-filter basis in ‘factor’ mode.
factor_max_lag – Maximum learned per-series response lag in ‘factor’ mode. Defaults to 0 (contemporaneous)
factor_config – Dict overriding factor_network.DEFAULT_FACTOR_CONFIG.
coherence_config – Post-forecast coherence-shrink settings. Merged over
factor_config['coherence_config']; supplying it also enables the shrink unlessfactor_configset ‘coherence’ explicitly. None (default) leaves the shrink off.derived_definitions – Declared ratio identities {column: (num, den)}; never inferred from column names.
factor_deconfound_edges – Re-run series edge discovery deconfounded against the learned factors (‘factor’ mode). Off by default
structure_learning_config – Dict enabling DAG and dynamic hierarchy learning in the V2 trend network.
- fit(df: DataFrame) TVA¶
Full TVA pipeline: decompose, build priors, train network.
- Parameters:
df – Wide DataFrame with DatetimeIndex and numeric columns.
- Returns:
self
- forecast_covariance(horizon: int = None)¶
(N, N) raw-unit cross-series forecast-error covariance, or None.
One covariance object for the callers that each otherwise invent their own weighting: MinT’s
W, the closed-form what-if solver, and (later) interval coherence. It is built from the rolling-origin residual matrix the trend model already forms, blended toward a low-rankLambda Sigma_f Lambda' + diag(psi)target, and floored at the same per-series sigma the shipped prediction intervals use.Only the modes whose trend model is linear in a small set of factors are served:
trend_network='factor'(loadings from the fitted factor model) and'none'(loadings from structure discovery, residuals from rolling the damped-trend rule).'v1'/'v2'return None — those already have_compute_recent_reconciliation_residuals()for reconciliation and the backprop scenario solver for what-if, and a covariance built from a different model than the one forecasting would misdescribe them.- Parameters:
horizon – forecast horizon the covariance should describe. Defaults to
forecast_horizon.- Returns:
(sigma, info)withsigmaan (N, N) covariance in the raw units of the input data andinfoa diagnostics dict carryingalpha(shrinkage intensity),beta(structural scale),psi,floor_bindingandn_samples. ReturnsNonewhen unfitted, when the mode is not served, or when no usable residual matrix or loadings exist — callers degrade to their previous behaviour rather than seeing an exception.
- get_composite_trends() dict¶
Return learned composite/prototype trends for inspection.
- Returns:
Dict with ‘prototypes’ (K, D), ‘composite_trend’ (n_global, T_forecast), and ‘prototype_weights’ (N, K).
- get_edges() DataFrame¶
Return the discovered edge table over real series names (W-7).
These are predictive screening results, not causal claims. An edge means “after removing the shared factors, this series’ history helped predict that one’s, stably across subsamples” — useful for screening, not evidence of mechanism. Series edges stay disabled for forecasting until they clear the false-positive/lag-recovery/follower-forecast gates together; the trustworthy structural output today is the factor -> series loading graph (
get_factor_graph/get_factor_diagnostics’sloading_graph).Columns: source, target, lag, sign, weight, family, stability, delta_mse. Empty DataFrame when discovery was disabled or found nothing. In trend_network=’factor’ mode the induced factor -> series bipartite edges (family ‘factor’) are appended; see get_factor_graph.
- get_factor_diagnostics() dict¶
Everything the factor mode decided, in one JSON-safe dict.
- Returns:
dict with the selected rank, factors/loadings, anchor/responder masks, stability scores, continuation choice, per-series blend weights, re-anchor alphas, gated series (by reason), the learned loading graph, validation scores and the coherence adjustment magnitude. Keys are absent rather than fabricated when the corresponding mechanism was not enabled.
- get_factor_graph() DataFrame¶
Induced factor -> series bipartite graph (‘factor’ mode).
One row per (factor, series) pair with a non-negligible loading, giving the signed loading weight and the series’ learned response lag. This is the structure the latent-factor mode actually uses to forecast, as opposed to the series->series edges from discovery.
- Returns:
DataFrame with columns source, target, lag, sign, weight, family. Empty when not in ‘factor’ mode or not fitted.
- get_factors() dict¶
Return named composite factors and their sparse signed loadings (W-7).
- Returns:
Dict with ‘factors’ (DataFrame, one named column per factor, level space, indexed like the training data), ‘loadings’ (DataFrame, series x factor), and ‘factor_names’. In trend_network=’factor’ mode these are the learned level-space paths, plus ‘lags’, ‘variance_share’, ‘phi’ and ‘diag’.
- get_graph() ndarray¶
Return the adjacency matrix (learned for V2, prior for V1).
- Returns:
(M, M) numpy array.
- get_graph_snapshot(threshold: float = None, include_priors: bool = True) dict¶
Return a serializable snapshot of the learned graph and hierarchy.
- plot_graph(view: str = 'dag', threshold: float = None, max_edges: int = 50, show_priors: bool = False, ax=None, metadata_color_by: str = 'auto')¶
Plot the learned graph, hierarchy, or adjacency heatmap.
- predict(forecast_length: int = None) DataFrame¶
Generate forecasts for all series.
- Parameters:
forecast_length – Number of future periods. Defaults to forecast_horizon.
- Returns:
Wide DataFrame (forecast_length, N) with forecasted values.
- reconcile(forecasts: DataFrame = None, residuals: ndarray = None, aggregate_sigma=None) DataFrame¶
Apply hierarchical reconciliation if configured.
Note
When
forecastsholds only the bottom level, this method builds the aggregate rows by summing it through S — which places the input exactly in the coherent subspace MinT projects onto, so reconciliation returns it unchanged for any W, in any trend mode. Reconciliation can only move a number when the aggregate levels carry a forecast that was not derived from the bottom one; pass a full (L-column)forecastsframe, ordered aggregates-then-bottom, to supply one.- Parameters:
forecasts – Forecast DataFrame. If None, generates fresh predictions. Bottom-level columns only, or all L hierarchy levels with the aggregate columns first (see the note above).
residuals – Optional historical residual matrix for all hierarchy levels. If omitted, TVA estimates recent one-step residuals from the last 30-90 training windows so covariance-aware reconciliation methods can use a meaningful W matrix.
aggregate_sigma – Optional (n_agg,) forecast-error standard deviations for independently-produced aggregate forecasts. Only consulted on the structural-covariance path, where it is what stops Sigma from cancelling out of MinT’s estimator (see
_structural_reconciliation_W()).
- Returns:
Reconciled DataFrame.
- what_if(**constraints) DataFrame¶
Scenario planning: adjust the forecast to satisfy user constraints.
Two solvers behind one signature, picked by what the fit produced.
trend_network='v1'/'v2'backpropagate a latent perturbation through the trend network (BifrostOptimizer).'factor'and'none'have no network to backprop through — the optimizer used to dereferenceself._networkand raise there — but their forecast is linear in the factor paths, soClosedFormScenariosolves the same minimum-disruption problem in closed form underforecast_covariance().- Parameters:
**constraints – Passed to the solver’s methods. Supported keys: - series_name, timestep, target_value -> apply_constraint - series_name, growth_rate -> apply_growth_constraint - level_name, target_value -> apply_hierarchical_constraint
- Returns:
Adjusted forecast DataFrame.
- class autots.evaluator.tva.YggdrasilPriors(series_metadata: list = None, relationship_matrix: ndarray = None, prior_confidence: float = 0.3, detected_features: dict = None, trend_data: DataFrame = None, observed_history: Dict[str, int] = None, prior_construction_config: dict = None, causal_prior_construction_config: dict = None, series_names: list = None)¶
Bases:
objectConstructs prior adjacency matrices, metadata embeddings, and hierarchy matrices from series metadata. All methods return sensible defaults when metadata is absent, so TVA runs with or without priors.
- Parameters:
series_metadata – List of SeriesMetadata, one per series.
relationship_matrix – Optional (N, N) soft prior adjacency override.
prior_confidence – Weight of prior vs learned structure (0=ignore, 1=rigid).
- build_hierarchy_matrix() ndarray¶
Build summing matrix S for hierarchical reconciliation.
S has shape (L, M) where L = total nodes (aggregates + bottom), M = number of bottom-level series. Compatible with mint_reconcile(S, y_all, W).
Returns identity if no hierarchy paths are specified.
- build_metadata_embeddings() ndarray¶
Build (N, D_meta) one-hot embedding matrix from categorical metadata.
Encodes arbitrary categorical metadata keys as concatenated one-hot vectors. Returns zeros if no metadata is available.
- build_prior_adjacency() ndarray | None¶
Backward-compatible alias for structural prior construction.
- build_structural_prior_adjacency() ndarray | None¶
Construct a soft structural prior from explicit, metadata, and event sources.
- get_anchor_mask(min_history: int) ndarray¶
Return boolean mask (N,) where True = series has enough history to be an anchor.
Anchors shape the core latent trend graph. Responders inherit composite trends but do not perturb the core geometry.
- get_series_names() list¶
Return ordered list of series names.
- property n_series¶
- autots.evaluator.tva.coerce_prior_adjacency(prior, series_names) ndarray | None¶
Normalize any supported user prior spec into a signed (N, N) matrix.
The single entry point for user-supplied graph priors, so every downstream consumer reads one shape regardless of how the prior was expressed.
Supported forms:
None->None(N, N)array-like -> validated and passed throughpd.DataFrame-> reindexed ontoseries_names(order-safe)list of
{'source', 'target', 'weight', 'directed'}dicts -> symmetric unless the row setsdirected: Truelist of name groups (
[['a', 'b'], ...]) -> each group is a clique at weight 1.0;{'series': [...], 'weight': 0.6}for a weaker group
Unknown series names raise a
RuntimeWarningnaming them and are dropped, never silently ignored.- Parameters:
prior – user-supplied prior in any of the forms above.
series_names – panel column order defining the matrix axes.
- Returns:
(N, N) float32, signed, clipped to [-1, 1], hollow diagonal; or None.