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
- A target-free observational table. The example uses the Iris measurements you
downloaded in Your first prediction (
dataandtarget). - A connected Welt workspace. See Connect your workspace.
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.
- CDFM does not relabel an acyclic directed result as a declared DAG.
- AVICI's native PRNG key stays 0; request seeds are retained as provenance. It also refuses every non-declared-DAG Ergodic conversion.
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.
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.
discover(X)andsubmit_discover(X)accept a DataFrame, a 2D array, CSV or Parquet through the existing table converter. Or pass the keyworddataset_idwithout uploading. Supply exactly one table or reference.- Arrays receive
x0,x1, … names. Named columns keep their original order. - Blocking
discovercloses its temporary HTTP client. A submitted job owns its client until you finish or reconnect and closejob.clientexplicitly.
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]"
uv add "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.
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:
client.delete_causal_result(result.id)explicitly deletes the retained primary graph and scores.client.delete_dataset(result.dataset_id)separately deletes an upload, only if it has no retained predictor or result, and no queued or running job dependencies.
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#
- Discover in Welt, estimate in ergodic identify and estimate effects on a discovered graph.
- Notebook examples the causal notebook inspects the real data first.
- Models and limits every qualified causal model and its limits.
- Wait and recover reconnect to jobs and handle typed errors.