A hidden ground-truth gene regulatory network (10 genes, directed activating/repressing edges) drives a Hill-function ODE system. Each gene's rate of change depends on its regulators:
dXi/dt = Σj wij · Xj^n/(K^n+Xj^n) − γ·Xi
The sign of wij (positive = activator, negative = repressor) already encodes the direction of regulation, so the same saturating Hill term applies to both: a repressor's pull only grows as its own concentration rises, and vanishes when the repressor is absent — never the reverse.
You never see this network directly — only noisy expression snapshots sampled from the simulated time series, exactly as an RNA-seq experiment would produce. Three inference strategies reconstruct edges from that data:
- Correlation — Pearson correlation between every gene pair, thresholded. Fast, but a shared regulator or hidden confounder makes two unrelated genes co-vary, producing spurious ("false positive") edges — the classic confounding problem.
- Partial correlation — correlation between gene i and j after regressing out every other gene's effect (a linear stand-in for multivariate regression / Granger-style causal inference). Removes most indirect edges that pure correlation keeps.
- Perturbation (CRISPR-style) — the ground truth for validation: each gene is knocked out in turn and the resulting expression shift in every other gene is measured directly, exactly like a pooled CRISPR screen or TF-binding assay. This is slower (needs one experiment per gene) but is the only method immune to confounding, because it tests causality directly rather than inferring it from covariation.
Edges are colored by outcome against the hidden ground truth: green = correctly recovered, red = false positive (inferred but not real — often a confounder artifact), gray, dashed = false negative (real edge the method missed). Precision = TP/(TP+FP), Recall = TP/(TP+FN). Raise noise or shorten the time series to see accuracy degrade; toggle the confounder off to see correlation's false-positive rate collapse.
Fix vs. the 3D version of this lab: the original engine implemented a repressor's Hill term as w·(1−hill(xj)), which peaks at xj = 0 and vanishes as xj grows — the opposite of real repression (verified numerically: sweeping a repressor's own concentration from 0.01→2.0 made its target's steady state rise from 0 to ~1.68 instead of falling). This build uses w·hill(xj) uniformly, since the sign of w already encodes activator vs. repressor — the target's steady state now correctly falls as the repressor's concentration rises.