Speed on your machine#
Run PC with Andrey and causal-learn on the same observations. The size ladder is deliberately small; increase it to explore your machine’s limits. Each fit runs sequentially with one numeric thread. Imports, warm-up, graph conversion, and accuracy scoring are outside the timed region.
The fastest causal discovery package. That claim comes from the release cards, not a promised result on this machine. The live demo labels its server; your hardware and data determine your times.
Run in a fresh kernel. Outside Colab, the install cell adds causal-learn with uv.
In Colab, these are timings of the Colab runtime. Download the notebook to measure your own CPU.
Start#
Choose a CPU runtime and run this cell first. In Colab it installs andrey-core==0.9.0 from PyPI;
local kernels use the installed environment.
import os
import sys
IN_COLAB = "google.colab" in sys.modules
if IN_COLAB:
os.environ["ANDREY_DEVICE"] = "cpu"
!pip install --quiet "andrey-core[viz]==0.9.0" pandas "causal-learn==0.1.4.8"
if not IN_COLAB:
!uv pip install --python {sys.executable} causal-learn==0.1.4.8
import os
for variable in (
"OMP_NUM_THREADS",
"OPENBLAS_NUM_THREADS",
"MKL_NUM_THREADS",
"NUMEXPR_NUM_THREADS",
"NUMBA_NUM_THREADS",
"ANDREY_NUM_WORKERS",
):
os.environ[variable] = "1"
os.environ["ANDREY_BACKEND"] = "numpy"
os.environ["ANDREY_DEVICE"] = "cpu"
import platform
from importlib.metadata import version
from time import perf_counter
import numpy as np
import pandas as pd
from causallearn.search.ConstraintBased.PC import pc as causal_learn_pc
from threadpoolctl import threadpool_limits
import andrey
import andrey.metrics as metrics
from andrey.data import benchmarks
print("Execution environment:", "Google Colab runtime" if IN_COLAB else "Local kernel")
print(f"Hardware: {platform.machine()}, {os.cpu_count()} logical CPUs; one thread per fit")
print(f"Andrey {andrey.__version__}; causal-learn {version('causal-learn')}")
def convert_causal_learn(result):
# causal-learn stores -1=tail, 1=arrow at the same matrix positions as Andrey.
native = result.G.graph
marks = np.zeros(native.shape, dtype=np.uint8)
marks[native == -1] = 1
marks[native == 1] = 2
return andrey.GraphStructure.from_numpy(marks, kind="cpdag")
Warm both packages, then time fits#
Both use stable PC, Fisher-Z, and alpha=0.05. The same three seeds are used at each size.
Speed and accuracy are reported separately: every paired ratio, and each package’s SHD beside
the empty graph’s.
sizes = [10, 20, 40]
seeds = [7, 11, 19]
rows = []
with threadpool_limits(limits=1):
warm = benchmarks.scm("linear_gauss_er", 5).sample(n=100, seed=0).data
andrey.pc(warm)
causal_learn_pc(warm, alpha=0.05, stable=True, show_progress=False)
for d in sizes:
for seed in seeds:
sample = benchmarks.scm("linear_gauss_er", d).sample(
n=10 * d, seed=seed, scale="standardize"
)
empty = andrey.GraphStructure.from_numpy(np.zeros((d, d)), kind="cpdag")
floor = metrics.score(empty, sample.graph)["shd"]
for package in ("Andrey", "causal-learn"):
start = perf_counter()
if package == "Andrey":
result = andrey.pc(sample.data, alpha=0.05)
else:
result = causal_learn_pc(
sample.data, alpha=0.05, stable=True, show_progress=False
)
seconds = perf_counter() - start
graph = result.structure if package == "Andrey" else convert_causal_learn(result)
shd = metrics.score(graph, sample.graph)["shd"]
rows.append(
dict(d=d, seed=seed, package=package, seconds=seconds, SHD=shd, empty_SHD=floor)
)
measurements = pd.DataFrame(rows)
measurements
summary = measurements.groupby(["d", "package"])[["seconds", "SHD", "empty_SHD"]].median()
display(summary)
paired = measurements.pivot(
index=["d", "seed"], columns="package", values=["seconds", "SHD", "empty_SHD"]
)
ratios = paired["seconds"]["causal-learn"] / paired["seconds"]["Andrey"]
ratios.groupby(level="d").agg(["median", "count"]).rename(
columns={"median": "Paired speed ratio", "count": "Paired seeds"}
)
Ratios are calculated per shared dataset and then aggregated. They need not equal ratios of median times. Increase the ladder only when the preceding sizes finish comfortably; causal-learn can take minutes at larger sizes.
The headline workload is PC at 400 variables and 4,000 observations. Add 400 to sizes only
when ready for a longer run. The benchmark page records the campaign builds and hardware; this
notebook measures the installed packages with one numeric thread on your machine.