# World DAG

The World DAG is Orxaq's central data structure. It is a directed acyclic graph where:

- **Nodes** are entities (variables) with typed metadata
- **Edges** are causal mechanisms with confidence and provenance

Every analysis in Orxaq — PD modeling, ECL computation, stress testing, fair lending, causal discovery — operates on or through the World DAG.

## Structure

```mermaid
graph LR
    GDP[GDP Growth] --> Income
    Unemployment --> Income
    Unemployment --> Default[Default Flag]
    Income --> Credit[Credit Score]
    Income --> DTI
    Credit --> Default
    DTI --> Default
```

Each node is an `Entity` with:

- `entity_id` — Unique string identifier (e.g., `"unemployment_rate"`)
- `name` — Human-readable name
- `causal_role` — `EXOGENOUS`, `MEDIATOR`, or `OUTCOME`
- `variable_type` — `CONTINUOUS`, `BINARY`, `CATEGORICAL`, `ORDINAL`

Each edge is a `CausalEdge` with:

- `source`, `target` — Entity IDs
- `mechanism` — Description of the causal process
- `confidence` — Expert-assigned confidence (0.0 to 1.0)
- `soliton_score` — Stability under perturbation (0.0 to 1.0)
- `provenance` — Origin of the edge (e.g., `"expert"`, `"pc_algorithm"`, `"consensus"`)

## Key Operations

**Topological Sort** — Returns entities in causal order. Macro variables come first, outcomes last. Used by the synthetic data generator and PD model.

**D-Separation** — Given two variables and a conditioning set, determines whether they are conditionally independent under the DAG. Uses the Bayes-Ball algorithm. Central to validation and falsification.

**Backdoor Adjustment** — Computes the minimal set of variables to condition on for unbiased causal effect estimation between two variables. Blocks all backdoor paths while keeping directed paths open.

**Path Enumeration** — Lists all directed paths between two nodes. Used by fair lending to identify indirect causal channels through mediators.

**Cycle Detection** — Adding an edge that would create a cycle raises `CycleError`. The DAG invariant is enforced at every mutation.

## Try It

```python
from orxaq.kernel import WorldDAG, Entity, CausalEdge, CausalRole, VariableType

dag = WorldDAG()
dag.add_entity(Entity("X", "Treatment", CausalRole.EXOGENOUS, VariableType.CONTINUOUS))
dag.add_entity(Entity("M", "Mediator", CausalRole.MEDIATOR, VariableType.CONTINUOUS))
dag.add_entity(Entity("Y", "Outcome", CausalRole.OUTCOME, VariableType.CONTINUOUS))

dag.add_edge(CausalEdge(source="X", target="M", mechanism="direct"))
dag.add_edge(CausalEdge(source="M", target="Y", mechanism="direct"))

# X and Y are NOT d-separated (open path X -> M -> Y)
assert not dag.d_separated("X", "Y", set())

# X and Y ARE d-separated given M (chain blocked)
assert dag.d_separated("X", "Y", {"M"})
```
