causal-automata
A blazing-fast, JAX-accelerated Cellular Automaton engine for causal discovery.
v0.3.0 — Corrected V-well / gravity / conditioning physics, JIT-safe run(ticks, k), and Maximum Accuracy Mode (--multihop, Directed F1 0.696 on N=100). See CHANGELOG.md.
Installation
pip install "causal-automata==0.3.0"
With CPU JAX:
pip install "causal-automata[jax]"
# or: uv pip install "causal-automata[jax]"
With NVIDIA CUDA JAX (and the Colab-style stack):
pip install "causal-automata[cuda]"
Or using uv in a project:
uv add "causal-automata[jax]"
Benchmark — N=100
Default stack (Upgrade 2): k=3 if N≤50, k=5 if N>50, binary edges, 1-hop gravity. On N=100 (seed=0, static thresholds, ticks=100) that baseline hits Skeleton 0.937 / Directed 0.619 in about 2.8s.
Maximum Accuracy Mode — pass --multihop (Upgrade 4): 2-hop elevation gravity via A @ A lifts Directed F1 to 0.696 with essentially flat wall time.
| Algorithm | Skeleton F1 | Directed F1 | Wall Time (s) |
|---|---|---|---|
| PC (gcastle) | 0.968 | 0.813 | 528.631 |
| GOLEM (gcastle) | 0.577 | 0.403 | 251.182 |
| Bayesian CA (JAX, k=5, 1-hop) | 0.937 | 0.619 | ~2.9 |
Bayesian CA (JAX, k=5, --multihop) |
0.928 | 0.696 | ~2.9 |
PC / GOLEM wall times are Colab Tesla T4 (num_iter=20000). CA times are CPU JAX for the Upgrade-2 / Upgrade-4 A/B; at historical k=3 the same CA finished in 0.323s on T4 with Directed F1 0.483.
Opt-in flags (defaults stay fast & binary):
--multihop— Maximum Accuracy Mode (2-hop gravity, Directed F1 0.696 on this SEM).--fuzzy— continuousA ∈ [0, 1]soft birth/death (experimental; did not beat binary here).
Conditioning-set sweep (CA-only, 1-hop binary, same SEM):
| Algorithm | Skeleton F1 | Directed F1 | Wall Time (s) |
|---|---|---|---|
| Bayesian CA (JAX, k=3) | 0.812 | 0.483 | 2.314 |
| Bayesian CA (JAX, k=4) | 0.915 | 0.546 | 2.481 |
| Bayesian CA (JAX, k=5) | 0.937 | 0.619 | 2.871 |
| Bayesian CA (JAX, k=6) | 0.937 | 0.619 | 2.454 |
| Bayesian CA (JAX, k=7) | 0.937 | 0.619 | 3.049 |
| Bayesian CA (JAX, k=8) | 0.937 | 0.627 | 3.647 |
Background & Origin
causal-automata evolved from graphnet-automata, a sister project exploring the application of 1D cellular automata rules to dynamic network graphs. While graphnet-automata focuses on generalized structural evolution and terminal-based visualization, this repository specializes those concepts into a high-performance, JAX-accelerated engine specifically designed for causal discovery and DAG generation.
Documentation
Full docs (theory, physics, API, CLI, JAX static ticks / k): https://causal-automata.readthedocs.io/en/latest/
How it works — CA physics + V-well
State is a binary DAG adjacency A, node elevations E ∈ [0,1], and a fixed empirical covariance Cov from continuous observations. Each tick is a local cellular update:
- Death / birth — prune weak edges and add strong ones using partial correlations conditioned on the top-
kunion neighbors ranked by|Cov|, after masking candidates with elevation below both endpoints (blocks collider / M-bias inS_{i,j}). Birth orients by elevation after the|ρ|gate. Optional--fuzzykeeps continuous weights in[0, 1]and binarizes only after the final tick. - V-structure gravity well — unshielded triples
(i, k, j)that show explaining-away (Berkson): marginally weak|ρ_{ij}|but stronger|ρ_{ij|k}|mark middle nodekas a collider. Collider elevations are pushed belowmin(E_i, E_j) − 0.15; parents are gently buoyed — the classic V-structurei → k ← j. - Gravity — drift elevations toward the midpoint of parent/child means (roots anchor missing parents at
1.0, leaves missing children at0.0, isolates holdE), then keep only downhill edges with a tiny index tie-break. With--multihop, that drift also sees 2-hop neighbors fromA @ A(discounted pull) to help break Markov-equivalence ties.
The pure functional JAX port (jax_core.step / jax_core.run) is @jax.jit-compiled; run(..., ticks=..., k=...) marks ticks and k as static so jax.lax.scan length and (2+k)×(2+k) partial-corr blocks stay compile-time constants. See the docs page JAX static arguments for functools.partial patterns that avoid ConcretizationTypeError.
Run
# Synthetic DAG harness (NumPy default; use --backend jax for JIT)
python -m causal_automata.bayesian_synth --n 10
python -m causal_automata.bayesian_synth --n 50 --backend jax
python -m causal_automata.bayesian_synth --n 100
# Benchmark vs gCastle PC / GOLEM (default N=100, k=5, 1-hop)
python -m causal_automata.benchmark
python -m causal_automata.benchmark --n 100 --seed 0 --ticks 100
# Maximum Accuracy Mode (Upgrade 4) — Directed F1 0.696 on the N=100 SEM
python -m causal_automata.benchmark --n 100 --ca-only --k 5 --multihop
python -m causal_automata.benchmark --n 100 --ca-only --k 5 --compare-multihop
python -m causal_automata.benchmark --n 100 --ca-only --k-sweep 5,6,7,8
python -m causal_automata.benchmark --smoke # device checks + GOLEM init only
Smoke the JAX compile:
python -c "from causal_automata import warmup, run; print(warmup())"
Package layout
causal-automata/
├── causal_automata/
│ ├── __init__.py
│ ├── bayesian.py # NumPy Bayesian CA step
│ ├── bayesian_synth.py # Synthetic linear-DAG harness
│ ├── jax_core.py # JAX @jit step / run
│ └── benchmark.py # vs gCastle PC / GOLEM
├── docs/ # Sphinx / Read the Docs
├── tests/
├── .readthedocs.yaml # uv-based docs install (matches CI)
├── pyproject.toml
├── README.md
└── NOTICE
License / notice
See NOTICE. Copyright 2026 Masakazu Yamagiwa.
Metadata
Release files for causal-automata 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| causal_automata-0.3.0.tar.gz | 32.5 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| causal_automata-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 62.5 kB
Release files / causal_automata-0.3.0.tar.gz
| Download URL | causal_automata-0.3.0.tar.gz |
|---|---|
| Size | 32.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f71f1c3305df50efc616d02307b8ecb12e600a291c8fc7aca37443628722a0da
|
|
BLAKE2b-256 checksum How to use checksums |
2fc0faee8bd323cbb5cb1fad863494e13252429e5c3c40cb209edcf4937d4783
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.5 {"installer":{"name":"uv","version":"0.13.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / causal_automata-0.3.0-py3-none-any.whl
| Download URL | causal_automata-0.3.0-py3-none-any.whl |
|---|---|
| Size | 30.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
352e3476b46838ed6b10a4bcd71527de0a8b6acdf6aee8f120bc97a94f7204f2
|
|
BLAKE2b-256 checksum How to use checksums |
f0160647a85ef6653620bca272fdfda1f1c0646a136a5372881858c260007cae
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.5 {"installer":{"name":"uv","version":"0.13.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Security for causal-automata 0.3.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.