causal-automata
A blazing-fast, JAX-accelerated Cellular Automaton engine for causal discovery.
v0.2.0 — JAX @jit physics, dynamic k, and Maximum Accuracy Mode (--multihop, Directed F1 0.696 on N=100). See CHANGELOG.md.
Installation
pip install "causal-automata==0.2.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 |
Documentation
Full docs (theory, physics, API, CLI): 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:
- Gravity — keep only downhill edges (
E[parent] > E[child]), then drift elevations toward the midpoint of parent/child means. With--multihop, that drift also sees 2-hop neighbors fromA @ A(discounted pull) to help break Markov-equivalence ties. - V-structure gravity well — absent undirected pairs that share neighbors and are marginally independent (
|ρ|below the death threshold with empty conditioning set) mark shared neighbors as colliders. Those elevations are sunk (E -= 2·lr), so later birth prefers arrows into the collider — the classic V-structurei → k ← j. - Death / birth — kill weak edges and birth strong ones using partial correlations conditioned on the top-
kunion neighbors ranked by|Cov|. Birth orients by elevation after the|ρ|gate. Optional--fuzzykeeps continuous weights in[0, 1]and binarizes only after the final tick.
The NumPy prototype (bayesian.step) and the pure functional JAX port (jax_core.step, @jax.jit) share this physics.
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; 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 port of step
│ └── benchmark.py # vs gCastle PC / GOLEM
├── docs/ # Sphinx / Read the Docs
├── .readthedocs.yaml
├── pyproject.toml
├── README.md
└── NOTICE
License / notice
See NOTICE. Copyright 2026 Masakazu Yamagiwa.
Metadata
Release files for causal-automata 0.2.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.2.0.tar.gz | 27.1 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| causal_automata-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 55.1 kB
Release files / causal_automata-0.2.0.tar.gz
| Download URL | causal_automata-0.2.0.tar.gz |
|---|---|
| Size | 27.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c2940d56b3ab13b18949dc29f6efc74ed3cae0f87e84cb16c4980fb201d45bd9
|
|
BLAKE2b-256 checksum How to use checksums |
5c38eaf9a39eba72250124218a5bb22f1390f1407c0b46c75990ee52bb8a6c6e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.3 {"installer":{"name":"uv","version":"0.13.3","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.2.0-py3-none-any.whl
| Download URL | causal_automata-0.2.0-py3-none-any.whl |
|---|---|
| Size | 28.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
16466cdbe825dc29247dc4de69de719cf296e1d3edb53a6d5de1545fb310719c
|
|
BLAKE2b-256 checksum How to use checksums |
9235eb94013c7c069b3135bfc4c7372191e5c6953d28eeab0bc76a0efa8c3c3c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.3 {"installer":{"name":"uv","version":"0.13.3","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.2.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.