Python · chapter 3 of 13

Networks: motifs and random topologies

A network is just a signed adjacency array, so you can build one by hand, take a registered motif, or draw one at random.

By hand

import numpy as np
S = np.zeros((3, 3))
S[1, 0] = 1       # G1 activates G2
S[2, 1] = -1      # G2 represses G3
model = grn.build_model(S, labels=["A", "B", "C"])

Registered motifs

grn.motif(name) returns a Motif with attributes name, S, labels, description, and preset, the parameter overrides that grn.build_model applies automatically when it is given the motif name. grn.catalog() lists them.

NameGenesStructurePreset behaviour
"toggle"2u and v repress each otherBistable: Hill 2, alpha = 5
"repressilator"3lacI -\tetR -\cI -\lacILimit-cycle oscillator: Hill 4, alpha = 10
"nar"1x represses itselfFast, robust response
"par"1x activates itselfSlow response, possibly bistable
"ffl_coherent"3X -> Y, X -> Z, Y -> ZSign-sensitive delay (AND gate with multiplicative logic)
"ffl_incoherent"3X -> Y, X -> Z, Y -\ZPulse generator
"cascade"size (4)G1 -> G2 -> ...Linear activation chain
"sim"size (4)TF -> T1, T2, ...Single-input module
"bifan"4A, B -> C, DTwo regulators, two targets
"goodwin"3X -> Y -> Z -\XOscillator: Hill 8, alpha = 10
m = grn.motif("cascade", size=6)
model = grn.build_model(m)                         # labels kept, no preset
model = grn.build_model("cascade")                 # with the preset, size 4

The presets set k, vmax, K, hill, gamma, and basal where the textbook behaviour depends on them. You can override any of them through params.

grn.plot_motifs(): every registered motif in pathway notation.
grn.plot_motifs(): every registered motif in pathway notation.

Random topologies

S = grn.random_network(n, ...) draws a signed adjacency with n genes; S, info = grn.random_network(n, ..., info=True) also returns generator details.

ArgumentDefaultMeaning
topology"erdosrenyi""erdosrenyi", "scalefree", "smallworld", or "modular"
mean_degree2Expected number of regulators per gene
fraction_repressors0.4Probability that an edge represses
self_regulation0Probability that a gene regulates itself
modules, within_fraction4, 0.8Modular topology: number of modules and fraction of edges inside modules
rewire0.1Small-world topology: rewiring probability
seed0Seed
infoFalseAlso return dict(topology, modules, mean_degree)

How each topology is drawn:

After the topology, each edge is made repressing with probability fraction_repressors, and each gene gets a self-loop with probability self_regulation (sign drawn the same way). info["mean_degree"] reports the realised mean in-degree.

The four topologies at 40 genes and mean in-degree 2. Scale-free networks concentrate outgoing edges on a few hub regulators; modular networks show dense blocks with sparse links between them.
The four topologies at 40 genes and mean in-degree 2. Scale-free networks concentrate outgoing edges on a few hub regulators; modular networks show dense blocks with sparse links between them.
Out-degree (targets per gene) and in-degree (regulators per gene) at 300 genes. The scale-free generator produces hubs with tens of targets while keeping the in-degree narrow, which is how real transcription networks look.
Out-degree (targets per gene) and in-degree (regulators per gene) at 300 genes. The scale-free generator produces hubs with tens of targets while keeping the in-degree narrow, which is how real transcription networks look.
S, info = grn.random_network(60, topology="modular", modules=3,
                             mean_degree=2.5, seed=4, info=True)
info
np.count_nonzero(S), np.count_nonzero(S, axis=1)
Output
info = {'topology': 'modular', 'modules': 'array(60,)', 'mean_degree': np.float64(2.5166666666666666)}
edges = 151
in-degree = [1 3 2 2 3 3 3 3 2 4 4 3 1 1 2 3 3 4 0 4 1 5 4 3 1 9 3 1 1 5 5 2 2 2 0 1 2 3 1 4 4 2 1 3 0 1 4 4 0
 2 0 3 1 4 3 1 5 1 2 4]

Checking a network

A few one-liners that are useful before building a model:

np.count_nonzero(S)                    # number of edges
np.count_nonzero(S, axis=1)            # in-degree (regulators per gene)
np.count_nonzero(S, axis=0)            # out-degree (targets per gene)
np.nonzero(~S.any(axis=1))[0]          # source genes: constitutive, no regulators
np.count_nonzero(np.diag(S))           # self-regulating genes