Model

The model module provides the core BayesianMMM class for fitting Marketing Mix Models using Bayesian inference.

Core Model

BayesianMMM - Main model class.

This module contains the BayesianMMM class which orchestrates model building, fitting, and prediction.

Identifiability note (equifinality, critique.md §3.6)

The core model uses logistic saturation (a single sat_lam per channel) by default, honoring each channel’s configured SaturationConfig type (logistic / hill / michaelis_menten / tanh / none) and, by default, normalized adstock (AdstockConfig.normalize=True). Normalization folds the total carryover magnitude into the channel coefficient beta, so a channel’s adstock decay, saturation strength, and beta trade off against one another: visibly different parameter combinations can fit the data almost equally well. This is intrinsic to additive MMM, not a defect, but it means the per-channel decomposition is only weakly identified from observational spend alone. The framework’s primary remedy is experiment-calibrated coefficient priors (mmm_framework.calibration), which anchor beta to randomized evidence and thereby break the trade-off; weakly-informative priors and (for the Hill path) data-anchored kappa bounds help at the margin. See mmm_framework.transforms.adstock.adstock_weights() for the kernel-level discussion.

class mmm_framework.model.base.BayesianMMM(panel, model_config, trend_config=None, adstock_alphas=None, experiments=None, model_params=None)[source]

Bases: object

Bayesian Marketing Mix Model - Robust Implementation with Prediction Support.

This implementation prioritizes numerical stability: - All data is standardized before modeling - Adstock: by default a continuous in-graph kernel estimated per channel,

honoring each MediaChannelConfig.adstock (geometric, delayed, or Weibull). Set ModelConfig.use_parametric_adstock=False for the legacy fast path (fixed-alpha bank blended via a learned mix) — the previous default, kept for reproducing older fits; deserialized pre-change models retain their original behavior.

  • Saturation: logistic (1 - exp(-lam * x)) by default – the most stable choice – honoring each MediaChannelConfig.saturation type per channel (logistic, hill, michaelis_menten, tanh, or none)

  • Priors are carefully scaled for standardized data

  • Flexible trend modeling with GP, spline, and piecewise options

  • Support for prediction and counterfactual analysis

  • Save/load functionality for model persistence

Parameters

panelPanelDataset

Panel data from MFFLoader.

model_configModelConfig

Model configuration.

trend_configTrendConfig, optional

Trend specification.

adstock_alphaslist[float], optional

Fixed adstock decay values to pre-compute.

CONFIG_SCHEMA: type[BaseModel] | None = None

Per-model config schema (Pydantic) declaring bespoke fields + defaults. None on the base model (it is fully configured by ModelConfig). A subclass / garden CustomMMM sets this to a BaseModel subclass so it can declare its own settable, defaulted, validated parameters (e.g. a binomial awareness model’s number_of_trials); the agent/spec layer validates spec["model_params"] against it and passes the result to the model_params constructor argument, where the model reads self.model_params.<field> in _build_model. See technical-docs/custom-model-config.md.

DATASET_SCHEMA: type[DatasetSchema] | None = None

Per-model data schema (a DatasetSchema subclass) declaring the role mapping a bespoke family expects, mirroring CONFIG_SCHEMA for params. None on the base model (the default MMM roles: kpi→target, media→predictor, control→control). See technical-docs/flexible-dataset.md.

REQUIRED_ROLES: tuple[DatasetRole, ...] = ()

Dataset roles this model requires the data to provide. Enforced at construction (a clear ValueError when missing) only when non-empty — the base model leaves it empty so existing flows are never gated. A family that genuinely needs e.g. a target + predictors declares REQUIRED_ROLES = (DatasetRole.TARGET, DatasetRole.PREDICTOR).

REQUIRED_DATASET_CAPABILITIES: tuple[str, ...] = ()

Optional duck-typed data-capability needs (e.g. "HAS_INDICATORS", "GEO_PANEL"), checked against dataset_capabilities(). Enforced only when non-empty. Mirrors an estimand’s required_capabilities gating.

__init__(panel, model_config, trend_config=None, adstock_alphas=None, experiments=None, model_params=None)[source]
static dataset_capabilities(ds)[source]

Cheap, duck-typed flags about the data (no graph build).

Mirrors estimands.capabilities.model_capabilities but for the dataset: GEO_PANEL (geo/product dimensions), HAS_INDICATORS (indicator columns), HAS_TRIALS (a binomial-denominator column).

Return type:

set[str]

add_experiment_calibration(experiments)[source]

Register experiment likelihood terms and invalidate the built graph.

The experiments are folded into the model as likelihood terms the next time the graph is built (so call this before fit()). Returns self for chaining.

Return type:

BayesianMMM

compute_adstock_curves(l_max_default=8)[source]

Posterior-mean adstock kernel (lag weights) per channel.

Returns the actual estimated carryover shape for reporting, so a delayed or Weibull channel is rendered with its true (possibly humped) kernel rather than being forced to a geometric curve. Requires a fitted model; returns None before fitting.

Return type:

dict[str, ndarray] | None

property model: pymc.Model

Get or build the PyMC model.

get_prior(samples=500, random_seed=None)[source]

Sample from the prior distribution of the model.

Return type:

InferenceData

fit(draws=None, tune=None, chains=None, target_accept=None, random_seed=None, method=None, nuts_sampler=None, **kwargs)[source]

Fit the model.

By default this runs full NUTS MCMC. method="smc" runs tempered Sequential Monte Carlo instead — also an exact sampler (MMMResults.approximate stays False), slower than NUTS on well-behaved posteriors but robust to multimodality (it populates modes NUTS gets stuck in) and it estimates the log marginal likelihood (diagnostics["log_marginal_likelihood"]) for model comparison. The remaining methods are approximate — they fit in seconds and are intended for quickly checking a model (bad priors, broken geometry, pathological saturation/adstock) before committing to a full sample. Their uncertainty is not calibrated and convergence diagnostics (R-hat / ESS) do not apply, so do not use them for final inference. See technical-docs/sampling-failure-playbook.md for which method to reach for when a fit fails.

Parameters:
  • draws (int | None) – Posterior draws per chain (NUTS), particles per SMC run (SMC), or number of approximate draws to take from the fitted approximation. Default from config.

  • tune (int | None) – Number of tuning samples (NUTS only). Default from config.

  • chains (int | None) – Number of MCMC chains (NUTS) or independent SMC runs (SMC — R-hat is computed across them). Default from config.

  • target_accept (float | None) – Target acceptance rate for NUTS. Defaults to model_config.target_accept (itself 0.9 unless set via ModelConfigBuilder().with_target_accept(...)).

  • random_seed (int | None) – Random seed for reproducibility.

  • method (FitMethod | str | None) – Fit method — "nuts" (default, full MCMC), "smc" (Sequential Monte Carlo, exact), "map" (maximum a posteriori point), "laplace" (Gaussian approximation around the MAP point; via the declared pymc-extras dependency), "advi" / "fullrank_advi" (variational inference), or "pathfinder" (via pymc-extras — works out of the box). Defaults to model_config.fit_method.

  • nuts_sampler (str | None) – NUTS backend — "pymc", "numpyro", "nutpie" or "blackjax" (NUTS only; ignored by the other methods). Defaults to model_config.nuts_sampler, i.e. the sampler selected on the config via ModelConfigBuilder().bayesian_pymc() / .bayesian_numpyro() / .bayesian_nutpie().

  • **kwargs – Additional arguments passed to the underlying sampler (pm.sample for NUTS, pm.sample_smc for SMC).

Return type:

MMMResults

Returns:

Fitted model results with diagnostics. For approximate methods MMMResults.approximate is True (NUTS and SMC: False).

Raises:
  • NotImplementedError – If the config selects an inference method with no estimator behind it. Currently none do — both frequentist methods dispatch (see below) — but the refusal stays as the mechanism for a future declared-before-implemented value.

  • UnsupportedModelError – For a frequentist config whose model is not linear given fixed transforms.

Note

Paradigm dispatch happens first. inference_method selects the estimation paradigm (Bayesian vs frequentist); method / FitMethod selects among the Bayesian estimators and is ignored by the frequentist path, which has no MAP/ADVI/SMC analogue. A frequentist fit returns MMMResults with approximate=False and converged is None, and stamps diagnostics["inference_family"] = "frequentist" — every rendering surface branches on that, never on approximate.

predict(X_media=None, X_controls=None, return_original_scale=True, hdi_prob=0.94, random_seed=None)[source]

Generate predictions from the fitted model.

Return type:

PredictionResults

Parameters

X_medianp.ndarray, optional

New media data for counterfactual. If None, uses training data.

X_controlsnp.ndarray, optional

New control data. If None, uses training data.

return_original_scalebool

If True, returns predictions in original scale.

hdi_probfloat

HDI probability for uncertainty intervals.

random_seedint, optional

Random seed for reproducibility.

Returns

PredictionResults

Prediction results with samples and uncertainty bounds.

compute_component_decomposition()[source]

Decompose predictions into component contributions.

Return type:

ComponentDecomposition

Returns

ComponentDecomposition

Full component breakdown in original scale.

compute_counterfactual_contributions(time_period=None, channels=None, compute_uncertainty=True, hdi_prob=0.94, random_seed=None)[source]

Compute channel contributions using counterfactual analysis.

Return type:

ContributionResults

Parameters

time_periodtuple[int, int], optional

Time period (start_idx, end_idx) for contribution calculation.

channelslist[str], optional

List of channel names to compute contributions for.

compute_uncertaintybool

If True, computes HDI for contributions.

hdi_probfloat

Probability mass for HDI calculation.

random_seedint, optional

Random seed for reproducibility.

Returns

ContributionResults

Container with per-observation and total contributions.

compute_marginal_contributions(spend_increase_pct=10.0, time_period=None, channels=None, compute_uncertainty=True, hdi_prob=0.94, random_seed=None)[source]

Compute marginal contributions for a given spend increase.

When compute_uncertainty is True the marginal contribution and marginal ROAS are propagated through the full posterior (saturation and coefficient uncertainty included) and the returned frame gains HDI columns. This addresses critique.md §3.9: the headline efficiency number should never be reported as a bare point estimate.

Uncertainty requires the baseline and increased posterior-predictive draws to be paired so the sampled observation noise cancels in their per-draw difference; a single shared seed is used to guarantee that.

Return type:

DataFrame

predict_under(intervention, time_period=None, random_seed=None)[source]

Posterior-predictive outcome under a counterfactual intervention.

A thin wrapper over predict() that transforms X_media_raw per the intervention. time_period is accepted for interface symmetry (mmm_framework.estimands.spec.SupportsEstimands); windowing is applied by the estimand reducer, so the full series is returned here.

Return type:

PredictionResults

model_capabilities()[source]

Capability flags used to gate which estimands this model supports.

Return type:

set[str]

evaluate_estimands(estimands=None, *, random_seed=None)[source]

Realize estimands from the fitted posterior as mean + HDI.

estimands items may be Estimand instances, built-in names (resolved via the registry), or serialized dicts. With estimands=None uses declared_estimands if non-empty, else _default_estimands(). Returns a dict keyed by estimand name (wildcard-channel estimands expand to "{name}:{channel}"). Never raises for an unsupported estimand – it is returned with status="unsupported".

Return type:

dict[str, EstimandResult]

what_if_scenario(spend_changes, time_period=None, random_seed=None, compute_uncertainty=True, hdi_prob=0.94, max_draws=200)[source]

Run a what-if scenario with custom spend changes.

With compute_uncertainty (default), the outcome change carries DECISION uncertainty: the per-draw change in total media contribution over the window is evaluated from PAIRED posterior draws (same draws for baseline and scenario, no observation noise — the machinery the budget optimizer uses), yielding outcome_change_hdi and prob_positive = P(scenario beats baseline). A point estimate alone is not decision-grade in a budget meeting.

Return type:

dict

sample_channel_contributions(X_media=None, max_draws=None, random_seed=None)[source]

Posterior draws of per-channel contributions under a media scenario.

Evaluates the channel_contributions deterministic with X_media swapped in (training data when None), returning ORIGINAL-scale contributions of shape (n_draws, n_obs, n_channels). Because the model is additive in channels, a scenario that changes every channel at once still yields each channel’s own response — this is what makes budget-response curves cheap (one pass per spend level, not per channel × level).

max_draws thins the trace evenly to at most that many draws per chain-flattened posterior — response-curve grids don’t need the full posterior.

Raises NotImplementedError on a multiplicative specification: the additivity this method relies on holds on the LOG scale there, so the returned numbers are not original-scale contributions.

Return type:

ndarray

sample_latent_under(var_name, intervention=None, random_seed=None)[source]

Posterior draws of a named deterministic var_name under a counterfactual intervention on the media inputs.

Generalizes sample_channel_contributions() to any registered deterministic: set_data swaps in the intervention-transformed media (training media for Observed / None), then re-evaluates var_name via posterior-predictive. Returned shape is (n_draws, *var_shape) in the deterministic’s native (model) scale — the engine forms latent contrasts (intervention − baseline) from this, so any constant scale cancels. This is what powers latent-variable estimand contrasts (see mmm_framework.estimands.evaluate).

Return type:

ndarray

sample_prior_predictive(samples=500, random_seed=None)[source]

Sample from prior predictive distribution.

Return type:

InferenceData

compute_parameter_learning(var_names=None, *, prior_samples=1000, random_seed=None, **kwargs)[source]

Quantify how much the data updated each parameter relative to its prior.

Draws prior samples and compares them to the fitted posterior, returning the prior-to-posterior contraction, overlap, and location shift for every parameter – the honest way to tell whether a posterior reflects the data or merely re-states an informative/constrained prior. See mmm_framework.diagnostics.parameter_learning().

Return type:

DataFrame

Parameters

var_names:

Parameters to diagnose. None (default) uses the model’s free random variables (beta_*, sat_lam_*, adstock_*, intercept, …).

prior_samples:

Number of prior draws used to estimate the prior moments/overlap.

random_seed:

Seed for the prior draw (reproducibility).

**kwargs:

Forwarded to parameter_learning() (e.g. bins, c_strong, c_weak, ovl_dominated).

Returns

pandas.DataFrame

One row per parameter, sorted by contraction ascending (most prior-dominated first).

summary(var_names=None)[source]

Get posterior summary.

Return type:

DataFrame

save(path, save_trace=True, compress=True)[source]

Save the fitted model to disk.

Return type:

None

classmethod load(path, panel, rebuild_model=True)[source]

Load a saved model from disk.

Return type:

BayesianMMM

save_trace_only(path)[source]

Save only the fitted trace to a file.

Return type:

None

load_trace_only(path)[source]

Load a trace from a file into the current model.

Return type:

None

Results

Result containers for BayesianMMM.

This module contains the dataclasses used to return results from model fitting, prediction, and contribution analysis.

class mmm_framework.model.results.MMMResults(trace, model, panel, channel_contributions=None, diagnostics=<factory>, y_mean=0.0, y_std=1.0, estimands=<factory>, approximate=False)[source]

Bases: object

Container for fitted model results.

trace: InferenceData
model: Model
panel: PanelDataset
channel_contributions: DataFrame | None = None
diagnostics: dict
y_mean: float = 0.0
y_std: float = 1.0
estimands: dict[str, EstimandResult]
approximate: bool = False
property converged: bool | None

MCMC convergence verdict (R-hat / ESS / divergences).

True/False for NUTS fits; None when not assessable (an approximate MAP/ADVI fit, or no usable diagnostics). None is NOT “converged” – surface it as “N/A”. Do not act on intervals/ROI from a fit where this is False.

property convergence_flags: list[str]

subset of {divergences, rhat, ess}.

Type:

Which convergence checks failed

summary(var_names=None)[source]

Get posterior summary statistics.

Return type:

DataFrame

plot_trace(var_names=None, **kwargs)[source]

Plot trace diagnostics.

plot_posterior(var_names=None, **kwargs)[source]

Plot posterior distributions.

__init__(trace, model, panel, channel_contributions=None, diagnostics=<factory>, y_mean=0.0, y_std=1.0, estimands=<factory>, approximate=False)
class mmm_framework.model.results.PredictionResults(posterior_predictive, y_pred_mean, y_pred_std, y_pred_hdi_low, y_pred_hdi_high, y_pred_samples)[source]

Bases: object

Container for prediction results.

posterior_predictive: InferenceData
y_pred_mean: ndarray
y_pred_std: ndarray
y_pred_hdi_low: ndarray
y_pred_hdi_high: ndarray
y_pred_samples: ndarray
property n_samples: int
property n_obs: int
__init__(posterior_predictive, y_pred_mean, y_pred_std, y_pred_hdi_low, y_pred_hdi_high, y_pred_samples)
class mmm_framework.model.results.ContributionResults(channel_contributions, total_contributions, contribution_pct, baseline_prediction, counterfactual_predictions, time_period=None, contribution_hdi_low=None, contribution_hdi_high=None)[source]

Bases: object

Container for channel contribution results.

These results are computed via counterfactual analysis: contribution = prediction(all channels) - prediction(channel zeroed out)

Attributes

channel_contributionspd.DataFrame

Per-observation contributions, shape (n_obs, n_channels). Index matches the original panel index.

total_contributionspd.Series

Total contribution by channel (summed over time).

contribution_pctpd.Series

Contribution as percentage of total.

baseline_predictionnp.ndarray

Prediction with all channels present.

counterfactual_predictionsdict[str, np.ndarray]

Counterfactual predictions with each channel zeroed out.

time_periodtuple[int, int] | None

Time period used for calculation, if any.

contribution_hdi_lowpd.Series | None

Lower HDI for total contributions (if uncertainty computed).

contribution_hdi_highpd.Series | None

Upper HDI for total contributions (if uncertainty computed).

channel_contributions: DataFrame
total_contributions: Series
contribution_pct: Series
baseline_prediction: ndarray
counterfactual_predictions: dict[str, ndarray]
time_period: tuple[int, int] | None = None
contribution_hdi_low: Series | None = None
contribution_hdi_high: Series | None = None
summary()[source]

Get summary DataFrame.

Return type:

DataFrame

__init__(channel_contributions, total_contributions, contribution_pct, baseline_prediction, counterfactual_predictions, time_period=None, contribution_hdi_low=None, contribution_hdi_high=None)
class mmm_framework.model.results.ComponentDecomposition(intercept, trend, seasonality, media_total, media_by_channel, controls_total, controls_by_var, geo_effects, product_effects, total_intercept, total_trend, total_seasonality, total_media, total_controls, total_geo, total_product, y_mean, y_std, events=None, total_events=None, interactions=None, total_interactions=None, levers=None, total_levers=None)[source]

Bases: object

Container for full component decomposition results.

intercept: ndarray
trend: ndarray
seasonality: ndarray
media_total: ndarray
media_by_channel: DataFrame
controls_total: ndarray
controls_by_var: DataFrame | None
geo_effects: ndarray | None
product_effects: ndarray | None
total_intercept: float
total_trend: float
total_seasonality: float
total_media: float
total_controls: float
total_geo: float | None
total_product: float | None
y_mean: float
y_std: float
events: ndarray | None = None
total_events: float | None = None
interactions: ndarray | None = None
total_interactions: float | None = None
levers: ndarray | None = None
total_levers: float | None = None
summary()[source]

Get summary of component contributions.

Return type:

DataFrame

media_summary()[source]

Get detailed media channel breakdown.

Return type:

DataFrame

controls_summary()[source]

Get detailed control variable breakdown.

Return type:

DataFrame | None

__init__(intercept, trend, seasonality, media_total, media_by_channel, controls_total, controls_by_var, geo_effects, product_effects, total_intercept, total_trend, total_seasonality, total_media, total_controls, total_geo, total_product, y_mean, y_std, events=None, total_events=None, interactions=None, total_interactions=None, levers=None, total_levers=None)

Trend Configuration

Trend configuration classes for BayesianMMM.

This module contains the TrendType enum and TrendConfig dataclass used to configure trend components in the model.

class mmm_framework.model.trend_config.TrendType(value, names=<not given>, *values, module=None, qualname=None, type=None, start=1, boundary=None)[source]

Bases: str, Enum

Available trend specifications.

NONE = 'none'
LINEAR = 'linear'
PIECEWISE = 'piecewise'
SPLINE = 'spline'
GP = 'gaussian_process'
class mmm_framework.model.trend_config.TrendConfig(type=TrendType.LINEAR, n_changepoints=10, changepoint_range=0.8, changepoint_prior_scale=0.5, n_knots=10, spline_degree=3, spline_prior_sigma=1.0, gp_lengthscale_prior_mu=0.3, gp_lengthscale_prior_sigma=0.2, gp_amplitude_prior_sigma=0.5, gp_n_basis=20, gp_c=1.5, growth_prior_mu=0.0, growth_prior_sigma=0.5)[source]

Bases: object

Configuration for trend component.

Scale notes:

Trends enter the model on standardized y (z-scored) against time scaled to t in [0, 1], so a slope of 1.0 means the trend moves y by one standard deviation over the whole series. Default prior widths are chosen so that a realistic trend spanning ~1-2 sd of y sits within ~1-2 prior sd: growth_prior_sigma=0.5 for the linear slope, and changepoint_prior_scale=0.5 (Laplace scale of each Prophet-style slope change) for piecewise. The old, much tighter defaults (0.1 / 0.05) effectively pinned the trend near zero and pushed real trend/structural breaks into media and intercept.

type

Type of trend to use.

n_changepoints

Number of potential changepoints for piecewise trend (Prophet-style).

changepoint_range

Proportion of time range to place changepoints (0-1).

changepoint_prior_scale

Prior scale for changepoint magnitudes.

n_knots

Number of knots for spline trend.

spline_degree

Degree of B-spline (default 3 = cubic).

spline_prior_sigma

Prior sigma for spline coefficients.

gp_lengthscale_prior_mu

Prior mean for GP lengthscale (in proportion of time range).

gp_lengthscale_prior_sigma

Prior sigma for GP lengthscale.

gp_amplitude_prior_sigma

Prior sigma for GP amplitude (HalfNormal).

gp_n_basis

Number of basis functions for HSGP approximation.

gp_c

Boundary factor for HSGP (typically 1.5-2.0).

growth_prior_mu

Prior mean for linear growth rate.

growth_prior_sigma

Prior sigma for linear growth rate.

type: TrendType = 'linear'
n_changepoints: int = 10
changepoint_range: float = 0.8
changepoint_prior_scale: float = 0.5
n_knots: int = 10
spline_degree: int = 3
spline_prior_sigma: float = 1.0
gp_lengthscale_prior_mu: float = 0.3
gp_lengthscale_prior_sigma: float = 0.2
gp_amplitude_prior_sigma: float = 0.5
gp_n_basis: int = 20
gp_c: float = 1.5
growth_prior_mu: float = 0.0
growth_prior_sigma: float = 0.5
to_dict()[source]

Convert to dictionary for serialization.

Return type:

dict

classmethod from_dict(data)[source]

Create from dictionary.

Return type:

TrendConfig

__init__(type=TrendType.LINEAR, n_changepoints=10, changepoint_range=0.8, changepoint_prior_scale=0.5, n_knots=10, spline_degree=3, spline_prior_sigma=1.0, gp_lengthscale_prior_mu=0.3, gp_lengthscale_prior_sigma=0.2, gp_amplitude_prior_sigma=0.5, gp_n_basis=20, gp_c=1.5, growth_prior_mu=0.0, growth_prior_sigma=0.5)

Trend Components

Trend component strategies for BayesianMMM.

This module implements the Strategy pattern for building different types of trend components in the model.

class mmm_framework.model.components.trend.TrendStrategy(*args, **kwargs)[source]

Bases: Protocol

Protocol for trend building strategies.

build(model, data, config)[source]

Build the trend component.

Return type:

TensorVariable

Parameters

modelpm.Model

The PyMC model context.

dataPreparedData

Prepared data containing trend features.

configTrendConfig

Trend configuration parameters.

Returns

pt.TensorVariable

Trend component tensor, shape (n_obs,).

__init__(*args, **kwargs)
class mmm_framework.model.components.trend.BaseTrendStrategy[source]

Bases: ABC

Base class for trend strategies with common utilities.

abstract build(model, data, config)[source]

Build the trend component.

Return type:

TensorVariable

class mmm_framework.model.components.trend.LinearTrendStrategy[source]

Bases: BaseTrendStrategy

Simple linear trend: trend = k * t.

build(model, data, config)[source]

Build linear trend component.

Return type:

TensorVariable

class mmm_framework.model.components.trend.PiecewiseTrendStrategy[source]

Bases: BaseTrendStrategy

Prophet-style piecewise linear trend with changepoints.

The trend is modeled as:

trend(t) = (k + A @ delta) * t + (m + A @ (-s * delta))

Where: - k is the base growth rate - delta are the changepoint adjustments - A is the changepoint indicator matrix - s are the changepoint locations - m is the offset

build(model, data, config)[source]

Build piecewise linear trend component.

Return type:

TensorVariable

class mmm_framework.model.components.trend.SplineTrendStrategy[source]

Bases: BaseTrendStrategy

B-spline trend with random walk prior on coefficients.

This provides smooth, flexible trends without explicit changepoints.

build(model, data, config)[source]

Build spline trend component.

Return type:

TensorVariable

class mmm_framework.model.components.trend.GPTrendStrategy[source]

Bases: BaseTrendStrategy

Gaussian Process trend using HSGP (Hilbert Space GP) approximation.

This provides non-parametric, smooth trends with uncertainty quantification, while remaining computationally efficient.

build(model, data, config)[source]

Build GP trend component using HSGP approximation.

Return type:

TensorVariable

class mmm_framework.model.components.trend.TrendBuilder[source]

Bases: object

Factory for building trend components.

Selects the appropriate strategy based on TrendType.

STRATEGIES = {'gp': <class 'mmm_framework.model.components.trend.GPTrendStrategy'>, 'linear': <class 'mmm_framework.model.components.trend.LinearTrendStrategy'>, 'none': None, 'piecewise': <class 'mmm_framework.model.components.trend.PiecewiseTrendStrategy'>, 'spline': <class 'mmm_framework.model.components.trend.SplineTrendStrategy'>}
classmethod build(model, data, config)[source]

Build trend component using appropriate strategy.

Return type:

pt.TensorVariable | None

Parameters

modelpm.Model

PyMC model context.

dataPreparedData

Prepared data with trend features.

configTrendConfig

Trend configuration.

Returns

pt.TensorVariable | None

Trend component, or None if trend_type is NONE.