WeltPython SDK
Products: switch from Welt to another product
Open console

GuidesCausal discovery

Causal discovery

Discover a causal graph from an observational table with a qualified model, then inspect its native edges and scores.

You'll need

Choose a model#

ArrowFM, CDFM and AVICI are observational previews. Arrow and CDFM take up to 8,000 rows × 20 variables, with measured wider shapes of 4,000 × 40 and 2,000 × 80. AVICI takes up to 1,000 × 20 (or 500 × 40). Every model needs at least 2 rows and 2 finite continuous variables. The service catalogue (client.models()) reports the current limits, shapes, version and readiness.

Model Select with Input and decoding Native output
ArrowFM model="arrow" Original native DAG decoder, with separate scores. Native DAG.
CDFM model="cdfm" Original native adaptive threshold, with separate native scores. Directed output, including possible cycles and reciprocal edges.
AVICI model="avici" Preserves full observations and the native population z-score; strict probability > 0.5 decoding. Directed output, including possible cycles and reciprocal edges.

You select every model through the same client interfaces.

Scores are not causal confidence

Native scores are not calibrated causal confidence or effects.

Other causal engines remain gated. These interfaces perform no automatic ergodic or model download.

Prepare observational data#

Use target-free observational datasets. Discovery preserves every uploaded column in its original order. It rejects target-labelled datasets instead of dropping their target. Nonempty constraints and unsupported input are rejected before model work.

The causal notebook downloads and inspects the real data first.

Iris has no ground-truth causal graph

Inspect native edges and assumptions, without pretending class labels evaluate a graph or establish causal truth.

Run discovery#

Client and AsyncClient provide submit_discover(dataset_id, model='arrow', configuration='default', seed=0, constraints=None, model_version=None, idempotency_key=None), plus a blocking (or awaitable) discover(..., timeout=600).

Check readiness first. Capability and readiness checks come from the current /v1/models task profile, not a client-side list or a model being present in the catalogue. The example drops the encoded class column, checks ArrowFM's causal_discovery profile, uploads the four measurements and runs discovery.

python
from welt import Client

# Continue after downloading/inspecting the real Iris data in Start.
# Exclude the encoded classification target; keep all four measurements/150 rows.
X = data.drop(columns=target)
print(X.head())
print(X.shape)
with Client() as client:
    profile = next(m for m in client.models() if m["id"] == "arrow")
    ready = any(p["task"] == "causal_discovery" and p["execution_available"]
                for p in profile["task_profiles"])
    if ready:
        dataset = client.upload(columns=list(X.columns), rows=X.values.tolist(), target=None)
        job = client.submit_discover(dataset["id"], model="arrow", seed=42)
        result = job.result()
        scores = client.causal_scores(job.id)
        reopened = client.causal_result(job.id)

Submission returns the existing durable Job or AsyncJob handle. A successful discover job returns a CausalResult; successful supervised jobs keep their current return types.

A timeout doesn't stop the job

Timeout does not cancel server work. Reconnect with client.job(job_id).

Use the estimator interface#

CausalDiscovery has sklearn-style constructor parameters, cloning and get_params. Its modelling operation is discover, not supervised fit and predict.

Read the result#

CausalResult preserves immutable ordered variables, marks, the native graph type, model and configuration, and an opaque score reference. to_dict returns a fresh ordinary wire dictionary. The immutable job_id, diagnostics, configuration_version, assumptions and decoder accessors expose the original provenance.

Blocking convenience calls return a result with result.job_id, so a new client can fetch scores or reopen it after the facade closes its HTTP client. Client reopening verifies this owned job identity. Use client.causal_result(job_id) and client.causal_scores(job_id) explicitly for authenticated reconnecting and score access. That way the result carries no hidden client or credentials.

Score access checks the result identity, reference, original axes and finite native probability semantics. Scores never threshold or rewrite the graph. Typed malformed-result errors apply without repair.

Convert to an Ergodic graph#

ergodic is Ergodic's open-source causal library. It identifies and estimates effects on a graph. Install it with the ergodic extra:

pip install "welt-client[ergodic]"

result.to_ergodic() converts a declared native DAG locally into an ergodic.MixedGraph. It keeps every isolate and the native tail and arrow directions. It refuses every graph not explicitly declared a native DAG, including acyclic directed graphs and cycles, without projection.

python
graph = result.to_ergodic()
print(graph.is_dag())

MixedGraph stores node sets. To keep the original axis order, inspect adjacency with graph.to_adjacency(order=list(result.variables)).

How is the ergodic dependency checked?

The extra installs ergodic>=0.2,<0.3. Conversion checks the graph format the installed package reads (ergodic.GRAPH_FORMAT, currently ergodic.graph/1), not its version. A missing package, or one without a supported format such as ergodic 0.1, raises a typed OptionalDependencyError that names the extra. Welt never downloads a dependency during conversion. CI tests conversion against the oldest and latest supported ergodic releases.

CDFM and AVICI results stay native here. ergodic can decode their edge scores into a DAG locally, but only when you pass an explicit decoder= to ergodic.welt.discover; the graph is then labelled decoded, not native. Check which versions work together in Compatibility with ergodic.

Continue in ergodic

The ergodic guide to Welt takes a converted graph through identification and effect estimation, and runs Welt models from inside ergodic.

Discovered edges are not causal truth

Conversion and local identification or effect estimation are separate scientific steps; a model's discovered edges do not establish causal truth. Native edge scores are neither calibrated confidence nor graph posterior probabilities.

Delete results and datasets#

Retention and deletion of the primary graph and scores, and result quotas, are server contracts. They are independent of seven-day downloadable prediction payloads. Two explicit calls delete owned state:

AsyncClient exposes the same methods with await. There is no implicit deletion, cascade or expiry when a client closes or discovery times out.

Situation Result
Successful deletion Returns None. Repeat deletion of the same owned resource is safe.
Foreign or missing identity Raises NotFoundError.
Dataset still has dependencies Raises ConflictError with code dependency_conflict.
Reading an explicitly deleted primary result or scores Raises ResultDeletedError. Job, provenance and usage metadata remain.
Payload removal returns retryable deletion_pending Retry the same DELETE. Quota and dataset dependency remain reserved until it completes.
How were the deletion routes checked?

These explicit server routes are deployed; dependency, race and tenant contracts were checked against temporary local state. Earlier bounded live acceptance retains its qualification results and does not delete hosted state.

Next steps#

Documentation

Search SDK 0.13.1

Search this SDK version by task, method or error.