Python · chapter 10 of 13
Recipes
Complete, copy-and-run workflows. Each one ends with data and its ground truth on disk.
1. A benchmark time-series dataset
Fifty cells of a 30-gene scale-free network with intrinsic and measurement noise.
import grnlab as grn
S = grn.random_network(30, topology="scalefree", mean_degree=2,
fraction_repressors=0.4, seed=2)
model = grn.build_model(S, logic="multiplicative", seed=2, name="sf30")
sol = grn.simulate(model, cells=50, random_ic=True, duration=20, sample_dt=0.25,
noise="langevin", noise_strength=0.2, noise_sigma=0.05, seed=2)
grn.export(sol, "sf30_timeseries.csv")
grn.export(sol, "sf30_timeseries.pkl")
grn.export_figure(grn.plot_network(model), "sf30_network.pdf")
grn.export_figure(grn.plot_expression(sol, cell=0), "sf30_expression.png")

2. A DREAM-style package: knockouts, knockdowns, and wild-type time series
model = grn.build_model(grn.random_network(10, seed=3), seed=3, name="net10")
ko = grn.knockout_screen(model, noise_sigma=0.02, seed=3)
kd = grn.knockout_screen(model, type="knockdown", noise_sigma=0.02, seed=4)
ts = grn.simulate(model, cells=10, random_ic=True, duration=20, noise_sigma=0.02, seed=5)
grn.export(ko, "net10_knockouts.csv")
grn.export(kd, "net10_knockdowns.csv")
grn.export(ts, "net10_timeseries.csv")
assert ko.converged.all() and kd.converged.all() # check before shipping
3. A dose-response experiment
Steady-state expression of every gene as a source gene is driven from 0 to 3 times its normal rate.
import numpy as np
model = grn.build_model(grn.motif("sim", size=5), seed=1) # keeps labels TF, T1..T4
doses = np.linspace(0, 3, 13)
X = np.array([grn.steady_state(model, stimulus=grn.stimulus("TF", "step", baseline=d, amplitude=d))
for d in doses])
import matplotlib.pyplot as plt
plt.plot(doses, X[:, 1:]); plt.xlabel("input to TF"); plt.ylabel("target expression")
A ramp gives the same information dynamically:
ramp = grn.stimulus("TF", "ramp", baseline=0, amplitude=3, start=0, stop=60)
sol = grn.simulate(model, stimulus=ramp, duration=80)
grn.plot_expression(sol)
4. Single-cell-like snapshots
Many cells, one sample each at a random time after a stimulus.
model = grn.build_model(grn.random_network(20, seed=6), stages=2, seed=6)
on = grn.stimulus(0, "step", amplitude=4, start=0)
sol = grn.simulate(model, stimulus=on, cells=200, random_ic=True,
noise="langevin", noise_strength=0.3, duration=10, sample_dt=0.1, seed=6)
rng = np.random.default_rng(6)
t_idx = rng.integers(sol.t.size, size=sol.cells) # one time per cell
cells_x = np.array([sol.mrna[t_idx[c], :, c] for c in range(sol.cells)]) # mRNA, like scRNA-seq
pseudotime = sol.t[t_idx]

5. Comparing motif variants
Coherent and incoherent feed-forward loops under the same step input.
on = grn.stimulus("X", "step", baseline=0, amplitude=1, start=2)
for name in ("ffl_coherent", "ffl_incoherent"):
model = grn.build_model(name, logic="multiplicative", seed=1,
params=dict(gamma=[5, 1, 3], vmax=5, basal=[5, 0, 0]))
sol = grn.simulate(model, stimulus=on, initial_condition=np.zeros(3), duration=20)
grn.export_figure(grn.plot_expression(sol), f"{name}.pdf")

6. A population of related networks
Ten networks that share a topology but differ in kinetics.
S = grn.random_network(15, seed=1)
for k in range(10):
model = grn.build_model(S, seed=k, name=f"net15_{k}")
sol = grn.simulate(model, cells=5, random_ic=True, seed=k)
grn.export(sol, f"net15_{k:02d}.pkl")
7. Sweeping a parameter of a motif
Where does the repressilator start to oscillate? (The analytic answer for Hill 4 is alpha = 2.)
alphas = np.arange(1.5, 3.01, 0.1)
amp = []
for a in alphas:
m = grn.build_model("repressilator", params=dict(k=a, vmax=a))
sol = grn.simulate(m, duration=100, transient=300)
amp.append(np.ptp(sol.x[:, 0]))
plt.plot(alphas, amp, "o-"); plt.xlabel("alpha"); plt.ylabel("amplitude")
