ergodicDocs
Products: switch from ergodic to another product
Get started

ReferenceWelt bridge

Welt bridge

Run Welt's hosted foundation models from ergodic. Import from ergodic.welt.

Welt hosts foundation models for tabular data and time series. This optional module connects them to ergodic's discovery, estimation and forecasting:

Install it with pip install "ergodic[welt]", which adds welt-client on Python 3.11 to 3.13 (welt-client does not support Python 3.14 yet). Every call here is a request to Welt and most are paid; nothing runs at import. The rest of ergodic never imports this module, welt or httpx, and makes no network calls.

Each function takes an optional client: a welt.Client you opened (it is used as is and never closed here). Without one, a short-lived welt.Client() is opened per call, reading the key from WELT_API_KEY or welt login.

Also exported from ergodic: discover.

NATIVE_QUANTILE_LEVELS#

python
NATIVE_QUANTILE_LEVELS: tuple = (0.01, 0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 0.99)

The 21 quantile levels Chronos-2 predicts natively (DEC-085 §1), the default request. Any strictly increasing subset of them is accepted.

WeltDecodeError#

python
class WeltDecodeError(
    model: str,
    decoder: str,
    error: Exception,
    result: Any,
    scores: pd.DataFrame | None,
) -> None

Decoding a paid Welt result's edge scores failed; the result is not lost.

ergodic checks every decoder setting before submitting, so this is rare. It carries the native result and its scores, which stay in your Welt workspace, so you can decode them again locally.

Attribute Description
model The Welt model id.
decoder The versioned decoder id that failed.
result The native welt.CausalResult.
scores Its edge-score matrix (rows the source, columns the target), or None if the scores themselves could not be read.

WeltForecastDiagnostics#

python
class WeltForecastDiagnostics(
    node: str,
    model: str,
    model_version: str,
    configuration: str,
    job_id: str,
    quantile_levels: tuple[float, ...],
    quantiles: npt.NDArray[np.float64],
    context_length: int,
    rearranged_steps: int,
    cross_learning: bool,
    multivariate: bool,
    conversion: str = 'inverse-cdf: linear between levels, exponential tails, steps independent',
    time_encoding: str = 'timestamps',
    point_semantics: str = 'median',
    calibrated_intervals: bool = False,
) -> None

Where one series' draws came from: the Welt job and its native quantiles.

One record per forecast node, in :attr:ForecastResult.diagnostics <ergodic.forecast.ForecastResult.diagnostics>. quantiles holds exactly what Welt returned for the node, shape (H, K) against quantile_levels, so the native numbers stay readable next to the draws made from them.

Attribute Description
node The forecast series.
model The Welt model id, for example "chronos-2".
model_version The exact model version Welt ran.
configuration The resolved configuration version Welt ran, as the job result reports it.
job_id The Welt job id.
quantile_levels The levels requested and returned.
quantiles Welt's quantiles for this node, (H, K), read-only.
context_length The history rows Welt read for this series.
rearranged_steps Steps of the whole job whose native quantiles crossed and that Welt sorted (DEC-085 §1); zero when nothing was repaired.
cross_learning Whether the series shared information.
multivariate Whether the targets were sent as one multivariate series.
conversion How the draws were made: inverse-CDF sampling, linear between levels, exponential tails, each step sampled on its own.
time_encoding "timestamps", or "steps-as-days" when the rows were sent as consecutive days from 2000-01-01 (a numeric index, or a datetime index on a business or custom calendar).
point_semantics Always "median": Welt's point forecast is the 0.5 quantile.
calibrated_intervals Always False: Chronos-2's quantiles are not calibrated. Check coverage with a backtest.
Field Type Default
node str required
model str required
model_version str required
configuration str required
job_id str required
quantile_levels tuple[float, ...] required
quantiles npt.NDArray[np.float64] required
context_length int required
rearranged_steps int required
cross_learning bool required
multivariate bool required
conversion str 'inverse-cdf: linear between levels, exponential tails, steps independent'
time_encoding str 'timestamps'
point_semantics str 'median'
calibrated_intervals bool False

WeltForecaster#

python
class WeltForecaster(
    model: str = 'chronos-2',
    *,
    targets: Sequence[str] | str | None = None,
    past_covariates: Sequence[str] | str = (),
    future_covariates: Sequence[str] | str = (),
    multivariate: bool = False,
    cross_learning: bool = False,
    quantile_levels: Sequence[float] = (0.01, 0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 0.99),
    draws: int = 1000,
    max_context: int | None = None,
    reconcile: str | None = None,
    client: Any = None,
    max_forecasts: int | None = 10,
    configuration: str = 'default',
    timeout: float = 600.0,
    name: str | None = None,
) -> None

Welt's hosted Chronos-2 as an ergodic forecasting model.

WeltForecaster().fit(data).forecast(12) forecasts every variable of data that is not a covariate (each must be continuous) 12 steps ahead with one paid Welt job and returns a ForecastResult of draws sample paths per series. backtest(WeltForecaster(), data, test_size=12) scores it, one paid job per fold. Chronos is the same class.

Welt answers each step with quantiles (all 21 native levels by default); the draws come from them by inverse-CDF sampling (quantile_draws()), seeded by forecast's seed. They match Welt's marginal at each step, but each step is sampled on its own: a path has no dependence across steps, and series have none between them. Each unreconciled node's per-step readings (median, intervals, coverage, CRPS) follow Welt's quantiles. A sum over steps, a bottom-up aggregate, and the intervals, coverage and CRPS of a bottom_up or mint reconciliation all treat the errors as independent across steps or series. The native quantiles stay in each node's WeltForecastDiagnostics.

fit is local and free: it checks the columns and keeps the window. Every forecast is a paid job. The first one also reads the model's catalogue entry (free) and checks the request against its measured forecast_envelopes before paying, and the model and configuration versions of the first job are required of every later one (X-Welt-Model-Version), so the folds of one backtest all run the same model. The forecaster and every copy of it (backtest copies it per fold) share one budget of max_forecasts jobs and one client. The budget counts jobs Welt accepted; a request it refuses (for example a 422) costs nothing and is not counted.

Parameter Description
model The Welt forecasting model id.
targets The columns to forecast. By default every variable that is not a covariate, or every node of the hierarchy for a HierarchicalTSData (an aggregate without a column is forecast from the sum of its children's history). On a hierarchy, an aggregate left out of targets is filled with the sum of its children's draws.
past_covariates Columns observed in the history only.
future_covariates Columns whose future values are known. forecast then needs future=; backtest supplies the held-out values. A categorical or ordinal column (by the dataset's schema) is sent as a categorical covariate, its cells as strings or integers (True becomes 1, 2.0 becomes 2); any other covariate must be continuous and is sent as numbers (a boolean as 0.0 or 1.0).
multivariate False sends each target as its own series of a panel; True sends them as the targets of one series, forecast jointly. Welt prices a forecast by series length times variates, so with covariates the multivariate request is the cheaper one.
cross_learning Let the series of a panel share information. False forecasts each as if it were sent alone.
quantile_levels The levels to request, a strictly increasing subset of NATIVE_QUANTILE_LEVELS with at least two levels.
draws Draws per series and step.
max_context Send at most this many of the most recent rows. None sends the whole window, and a window longer than the model's measured envelope is refused before paying.
reconcile On a hierarchy, reconcile the forecast before returning it: "bottom_up", "top_down" (historical shares of the training window) or "mint" (posterior weights). None returns the levels as forecast; reconcile() does the rest. The draws carry no dependence between series, so bottom_up and mint intervals (and the coverage and CRPS a backtest reads from them) treat the series as independent; top_down splits the root's draws by fixed shares and does not depend on it.
client A welt.Client to use as is, shared with every copy. None opens a short-lived welt.Client() per forecast.
max_forecasts The most paid forecasts this object and its copies may send; None removes the cap.
configuration The Welt model configuration.
timeout Seconds to wait for each forecast job. A timeout leaves the job running in Welt.
name What a backtest report calls the model; "welt:<model>" by default.
Attribute Description
forecasts_used Forecast jobs Welt accepted so far from this object and its copies.

WeltForecaster.future_covariates#

python
property WeltForecaster.future_covariates: tuple[str, ...]

The covariates whose future values forecast reads from future=.

WeltForecaster.client#

python
property WeltForecaster.client: Any

The welt.Client this forecaster and its copies use, or None.

WeltForecaster.forecasts_used#

python
property WeltForecaster.forecasts_used: int

Forecast jobs Welt accepted so far from this forecaster and every copy of it.

WeltForecaster.fitted#

python
property WeltForecaster.fitted: bool

True once fit() has run.

WeltForecaster.fit#

python
def WeltForecaster.fit(
    data: TimeSeriesData,
    *,
    seed: int = 0,
) -> WeltForecaster

Check the columns and keep the training window; nothing is sent.

Parameter Description
data The history, a TimeSeriesData or HierarchicalTSData with a datetime or an evenly spaced numeric time index (spacing compared with a relative tolerance of 1e-9).
seed Accepted for the protocol. Welt's forecast is deterministic, so only forecast()'s seed (for the draws) matters.

Returns: A fitted copy; this object is unchanged.

Raises Description
TypeError data is not a time series.
ValueError A named column is missing, a target is not numeric, a future covariate has missing values, or the time index is not usable.

WeltForecaster.forecast#

python
def WeltForecaster.forecast(
    horizon: int,
    *,
    seed: int = 0,
    future: TimeSeriesData | pd.DataFrame | None = None,
) -> ForecastResult

Forecast horizon steps past the training window: one paid Welt job.

Parameter Description
horizon Steps to forecast.
seed Seeds the draws made from Welt's quantiles.
future The future covariates over the horizon, horizon rows with the time column and every name in future_covariates; only those columns are read. Required exactly when there are future covariates. A TimeSeriesData or a dataframe.

Returns: A ForecastResult of (draws, horizon) draws per target, kind="draws", with one WeltForecastDiagnostics per node in diagnostics.

Raises Description
ValueError Not fitted, a bad horizon or future, or a request outside the model's measured envelopes (nothing is sent).
RuntimeError max_forecasts is spent, or welt-client is older than 0.14.
welt.WeltError Welt refused the job, or it failed (welt.ExecutionError) or was cancelled.
TimeoutError The job did not finish within timeout (welt.JobTimeoutError); it keeps running in Welt.

WeltGraphNotDAGError#

python
class WeltGraphNotDAGError(
    model: str,
    native_graph_type: str,
    result: Any = None,
    scores: pd.DataFrame | None = None,
) -> None

A Welt model's native graph is not a declared DAG, so ergodic refuses it.

ergodic converts only declared DAGs and never projects or relabels a directed graph as one. When the model is known to return such graphs, the refusal comes before anything is uploaded or paid for, and result is None. When a finished result turns out not to be a declared DAG, the paid result stays in your Welt workspace and this error carries it.

Attribute Description
model The Welt model id.
native_graph_type The model's native graph type, for example "directed".
result The native welt.CausalResult, or None if nothing was submitted.
scores Its edge-score matrix (rows the source, columns the target), or None if nothing was submitted.

WeltLearner#

python
class WeltLearner(
    model: str = 'tabicl-v2',
    task: str = 'regression',
    *,
    client: Any = None,
    max_fits: int | None = 10,
    configuration: str = 'default',
    seed: int = 0,
    timeout: float = 600.0,
) -> None

A Welt tabular model as an ergodic learner.

Use it wherever ergodic takes a learner, for example estimate_effect(graph, data, "X", "Y", strategy="dml", learner=WeltLearner("tabicl-v2")). A slot that needs probabilities (a propensity) takes a classification learner; pass a slot mapping such as {"outcome": WeltLearner(), "propensity": WeltLearner(task="classification")}.

Each fit uploads the training rows as a new Welt dataset and fits a predictor: one paid fit. Both stay in your Welt workspace afterwards; nothing here deletes them (welt.Client().predictors() and .datasets() list them). Each predict is a paid prediction, sent in batches no larger than the model's prediction-row limit. DML with n_folds=k fits 2 * k times.

A slot with no feature columns (an empty adjustment set) has nothing for a model to learn: its conditional mean is the training mean, or the class frequencies for classification. The learner fits that locally, with no upload, no paid fit and no charge to max_fits, and predictor_id_ is None.

The learner and its clones share one budget of max_fits fits. It caps paid fits only, not predictions: each fit is followed by one prediction call per batch of held-out rows (DML on 8,000 rows with five folds and a 100-row limit makes about 160). Fits take no row weights, so supports_sample_weight is False and ergodic refuses this learner in a weighted slot (the R-learner's final) before fitting anything.

Parameter Description
model Any Welt classification or regression model id; none is checked here. The default follows what Welt serves: "tabicl-v2" is available today, while "tabpfn-3.5-fast" is still planned. See which models run now, with their row, column and prediction limits, with [(m["id"], p["task"], p["limits"]) for m in welt.Client().models() for p in m["task_profiles"] if p["execution_available"]], welt models list on the command line, or the Models page of the Welt SDK docs.
task "regression" or "classification". Only a classification learner has predict_proba.
client A welt.Client to use as is, shared with every clone. None opens a short-lived welt.Client() per request.
max_fits The most paid fits this learner and its clones may make together; None removes the cap. The default 10 covers DML with five folds. Predictions are not counted.
configuration The Welt model configuration.
seed The fit seed Welt records with each predictor.
timeout Seconds to wait for each fit or pending prediction. A timeout leaves the job running in Welt.
Attribute Description
predictor_id_ The fitted Welt predictor's id; None after a no-feature (local) fit.
model_version_ The exact model version Welt fitted; None after a no-feature fit.
classes_ The class labels, sorted (classification only).
supports_sample_weight Always False; read by ergodic's weighted slots.

WeltLearner.get_params#

python
def WeltLearner.get_params(deep: bool = True) -> dict[str, Any]

The constructor arguments, as scikit-learn's get_params returns them.

Parameter Description
deep Accepted for scikit-learn compatibility; there are no nested estimators.

Returns: A dict of every constructor argument.

WeltLearner.set_params#

python
def WeltLearner.set_params(**params: Any) -> Self

Set constructor arguments; a new max_fits starts a fresh budget.

Parameter Description
**params Constructor arguments by name.

Returns: This learner.

Raises Description
ValueError A name is not a constructor argument, or a value is invalid.

WeltLearner.fits_used#

python
property WeltLearner.fits_used: int

Paid fits made so far by this learner and its clones together.

WeltLearner.fit#

python
def WeltLearner.fit(
    X: npt.NDArray[np.float64],
    y: npt.NDArray[np.float64],
    sample_weight: npt.NDArray[np.float64] | None = None,
) -> Self

Fit a Welt predictor on these rows: one paid fit.

Parameter Description
X The feature matrix, one row per unit. With no columns the fit is the local training mean (or class frequencies) and costs nothing.
y The target, one value per row: numbers for regression, labels for classification.
sample_weight Must be None. Welt fits take no row weights, and a weight is never silently dropped.

Returns: This learner, fitted.

Raises Description
ValueError sample_weight was given, X has no rows, or y does not match X. Nothing is uploaded.
RuntimeError The shared max_fits budget is spent.

WeltLearner.predict#

python
def WeltLearner.predict(X: npt.NDArray[np.float64]) -> npt.NDArray[Any]

Predict with the fitted Welt predictor: one paid prediction per batch.

Parameter Description
X Rows with the same columns as the fit.

Returns: Numbers in the target's units for regression, class labels for classification.

WeltLearner.predict_proba#

python
property WeltLearner.predict_proba: Callable[[npt.NDArray[np.float64]], npt.NDArray[np.float64]]

Class probabilities, one column per class in classes_ order.

Only a classification learner has this; ergodic reads its presence to tell a classifier from a regressor.

Raises Description
AttributeError The learner's task is regression.

discover#

python
def discover(
    data: TabularData,
    model: str = 'arrow',
    *,
    seed: int = 0,
    timeout: float = 600.0,
    knowledge: DomainKnowledge | None = None,
    decoder: str | Decoder | None = None,
    client: Any = None,
) -> DiscoveryResult

Learn a DAG with a Welt causal discovery model.

Uploads the dataset's variables (continuous, no missing values), runs one discovery job and waits for it. This is one paid Welt job; the uploaded dataset and the native result stay in your workspace until you delete them. ergodic.discover(data, method="welt:<model>") calls this.

Welt's models and limits come from its catalogue (welt.Client().models()). Today arrow declares native DAGs. cdfm and avici return native directed graphs that may hold cycles or reciprocal edges, so they are refused with WeltGraphNotDAGError before anything is uploaded or paid for (the catalogue's stated graph type wins over that built-in list). A result that is still not a declared DAG is refused too, with the paid result attached.

decoder= opts into local decoding. The run then accepts any model, reads the native edge scores (rows the source, columns the target) as EdgeProbabilities of kind "independent" (each score a separate directed-edge marginal, uncalibrated) with the Welt provenance, and decodes them with decode(). The returned graph is that decoded DAG, not the model's native graph, and its diagnostics say so. For the greedy decoder without an explicit threshold, the model's native threshold is used when Welt records one (decoder.parameters.threshold: 0.5 for AVICI, the adaptive threshold for CDFM), so greedy keeps what the model's own rule keeps and only drops edges that close a cycle or a reciprocal pair. On a DAG model (arrow) the decoder replaces the native DAG with one decoded from the same scores, read the same independent way. Any other choice can be made locally, with no new run, from result.diagnostics["edge_probabilities"]. Every decoder setting, the max_nodes cap against the data's variable count included, and the knowledge are checked before anything is uploaded; if decoding still fails after the paid run, WeltDecodeError carries the native result and its scores.

knowledge= without decoder= decodes with "greedy" at the model's native threshold, exactly as decoder="greedy" would: Welt runs unconstrained, and the knowledge (hard and soft statements, confounders) applies to the local decoding, with its audit in the diagnostics (see DecodeResult). A confound pair makes the decoded graph an ADMG.

Parameter Description
data Observational rows; every variable must be continuous and complete.
model The Welt causal discovery model id, for example "arrow".
seed The run's seed, recorded in the result's provenance.
timeout Seconds to wait for the job. A timeout leaves the job running in Welt; reconnect with welt.Client().job(job_id).
knowledge Domain knowledge for the local decoding (Welt itself takes no constraints); it implies decoder="greedy" when decoder is None. It is checked before anything is uploaded.
decoder None (native DAGs only, or greedy when knowledge is given), a decoder id such as "greedy", or a Decoder with its settings (a sampler needs Decoder("pair_sampler", samples=..., seed=...)).
client A welt.Client to use as is; None opens a short-lived one.

Returns: A DiscoveryResult whose graph is the native DAG with every variable, method is "welt:<model>", and whose diagnostics hold the Welt provenance: welt_job_id, welt_result_id, welt_dataset_id, welt_model, welt_model_version, welt_configuration_version, welt_decoder, welt_seed and welt_scores (the native edge scores as a dataframe, rows the source and columns the target; not calibrated confidence). With a decoder (or knowledge) it is a DecodeResult (still a DiscoveryResult, method "welt:<model>") whose graph is the decoded DAG (an ADMG with confounders); diagnostics add the decode labels (decoded True, native False, decoder, parameters, threshold_source, rejected_cycles, note, ...), welt_native_graph_type, welt_native_edges (the native directed edges as pairs) and edge_probabilities.

Raises Description
TypeError data is not tabular.
WeltDecodeError Decoding failed after the paid run; the error carries the result and its scores.
ValueError knowledge names unknown variables or contradicts itself, the decoder settings are invalid or its max_nodes is below the variable count, or a variable is not continuous or has missing values. Nothing is uploaded.
WeltGraphNotDAGError The model's native graph is not a declared DAG, either known before submitting (nothing is paid) or found in the result.

load_research_dataset#

python
def load_research_dataset(
    dataset_id: str,
    *,
    version: str | None = None,
    types: Mapping[str, VariableType | str | Ordinal] | None = None,
    exclude: Iterable[str] | None = None,
    categorical_threshold: int = 10,
    client: Any = None,
) -> TabularData

Load a dataset from Welt's research catalogue as tabular data.

Welt verifies the download's size and checksum and reads it into a dataframe; ergodic then infers the schema as tabular() does. Each download is billed per dataset (see Welt's pricing). Reading Parquet content needs pyarrow: pip install "welt-client[research]".

Parameter Description
dataset_id The research dataset's id, from client.research_datasets().
version A content version to pin; None takes the current one.
types Per-column type overrides, as in tabular().
exclude Columns to keep but not treat as variables.
categorical_threshold Integer columns with at most this many distinct values are read as categorical, with a warning.
client A welt.Client to use as is; None opens a short-lived one.

Returns: The table as TabularData.

upload_dataset#

python
def upload_dataset(
    data: TabularData,
    *,
    target: str | None = None,
    name: str = 'ergodic dataset',
    client: Any = None,
) -> dict[str, Any]

Upload a tabular dataset to your Welt workspace.

The upload holds the dataset's variables in their column order; excluded columns stay local. Values go up as they are stored (categories as their labels, missing values as empty cells). Uploading is free, and the dataset stays in the workspace until you delete it with client.delete_dataset(id).

Parameter Description
data The rows to upload.
target A variable to mark as the prediction target, for a later fit or evaluation. None uploads a target-free table, which is what causal discovery needs.
name The dataset name shown in Welt.
client A welt.Client to use as is; None opens a short-lived one.

Returns: The Welt dataset record, with its "id".

Raises Description
TypeError data is not a TabularData.
ValueError target is not one of the variables, or a cell is not a number or a string.
Suggest an edit
Documentation

Search ergodic 0.3.0

Search ergodic by task, function or error.