Tutorial 5 — Projects, schedules and stages (experimental)
Experimental
The stage library (roadmap phase C) is new and its API may still change. Every stage is a no-op with empty
parameters, stages that write delays clip them to max_delay (default: the storage bound of 255 ticks) with a warning, and grow_axons
raises an error when a projection has no guidance. Every stage’s parameters and known limits are listed in
Stage, geometry and field reference.
A schedule is data
A schedule is an ordered list of stages. Each entry names an operator (a registered stage), its params
(each a Param, see Core concepts), and optionally repeat and when.
from neurogenesis.stages.base import run_schedule
project = {
"version": "1.0.0", "fields": {}, "plugins": [],
"schedule": [
{"name": "seed", "operator": "seed_cells", "params": {"N": 20, "jitter": 5.0}},
{"name": "divide", "operator": "proliferate", "params": {"prob": 0.5, "jitter": 1.0}, "repeat": 3},
{"name": "death", "operator": "apoptosis",
"params": {"prob": {"type": "Gene", "path": ["death", "p"], "lo": 0.0, "hi": 0.5}}},
],
}
genome = {"death": {"p": 0.2}}
checkpoints = run_schedule(project, genome, seed=0, checkpoints=True)
print([int(t.alive.sum()) for t in checkpoints])
[20, 29, 43, 65, 56]
seed_cellsplaces 20 cells;proliferateruns three times (each cell divides with probability 0.5);apoptosisremoves cells with a probability read from the genome — an evolvable gene.checkpoints=Truereturns the tissue after every stage repetition, for previews and analysis.Each repetition has its own random stream derived from
(seed, stage name, repetition): inserting a stage does not change what the others draw.
One frame per checkpoint of a schedule: seed_cells (12 founders in a sphere) → proliferate ×5 (p = 0.6) → apoptosis (p = 0.15) → apoptosis by crowding → differentiate (every third clone inhibitory, triangles) → migrate ×3 (radial). Colour = clone. Generated by docs/_scripts/make_media.py.
A complete pipeline from JSON
Two regions seeded in boxes, divisions, a gene-regulatory step driven by a gradient, fates from the gradient, axons whose tuning width is a gene, myelination, activity-dependent refinement and pruning:
import json
import numpy as np
from neurogenesis.stages.base import run_schedule
from neurogenesis.spiking.batch import simulate_counts
gene = lambda path, lo, hi: {"type": "Gene", "path": path, "lo": lo, "hi": hi}
project = {
"version": "1.0.0", "plugins": [],
"fields": {"tissue": {"type": "Box", "center": [0, 0, 0], "size": [40, 40, 10]},
"target": {"type": "Box", "center": [60, 0, 0], "size": [40, 40, 10]},
"grad": {"type": "AnalyticField", "expr": "x / 40 + 0.5"}},
"schedule": [
{"name": "seed", "operator": "seed_cells", "params": {"N": 30, "geometry": "tissue", "region": 0}},
{"name": "seed2", "operator": "seed_cells", "params": {"N": 30, "geometry": "target", "region": 1}},
{"name": "divide", "operator": "proliferate", "params": {"prob": 0.5, "jitter": 1.0}, "repeat": 2},
{"name": "express", "operator": "express",
"params": {"K": 1, "morphogens": [{"field": {"type": "Field", "name": "grad"}, "weights": [1.0]}]}},
{"name": "fate", "operator": "differentiate", "params": {"types": [
{"type_id": 1, "condition": "field_grad < 0.5", "sign": 1},
{"type_id": 2, "condition": "field_grad >= 0.5", "sign": -1}]}},
{"name": "axons", "operator": "grow_axons", "params": {
"projections": [[0, 1]],
"guidance": {"0->1": {"B": [[1, 0, 0], [0, 1, 0], [0, 0, 0]], "c": [0, 0, 0],
"sigma": gene(["g", "sigma"], 0.05, 0.5), "p0": 0.8, "w": 300}},
"conduction_velocity": 2.0}},
{"name": "myelin", "operator": "myelinate", "params": {"factor": 0.5}},
{"name": "refine", "operator": "refine_by_activity", "params": {"ticks": 100}},
{"name": "prune", "operator": "prune", "params": {"threshold": 1}},
]}
project = json.loads(json.dumps(project)) # a project is plain JSON
tissue = run_schedule(project, genome={"g": {"sigma": 0.2}}, seed=0)
net = tissue.to_network()
ext = np.zeros((1, net.num_neurons), np.int32); ext[0, :5] = 100
for model in ("lif_int", "two_compartment", "hh"):
print(model, int(simulate_counts([net], [ext], ticks=100, model=model)[0].sum()))
lif_int 203
two_compartment 205
hh 27
The network has 131 cells and 319 synapses (166 inhibitory, from the type-2 cells), with delays of 10–22 ticks
after myelination. In this run refine_by_activity has no rule, so it leaves the weights unchanged.
Genes of a schedule
from neurogenesis.params import collect_genes
print(collect_genes(project["schedule"])) # [(path, low, high, integer), ...] — ready for evolution
Cost of a schedule
from neurogenesis.stages.base import schedule_cost
print(schedule_cost(project, N=10_000, E=1_000_000)) # (bytes, operations), declared by each stage
Available stages
Operator |
Task |
What it does |
|---|---|---|
|
C1 |
create cells at a point, in a geometry, with a region and type |
|
C2 |
cell division with a probability; daughters inherit lineage |
|
C3 |
cell death: by probability, density, or missing inputs / outputs |
|
C4 |
move cells (direction, radial, towards a target; stop field, boundary) |
|
C5 |
reorder cells along an axis by a key |
|
C6 |
gene-regulatory step on expression channels (morphogens, neighbours) |
|
C7 |
assign cell types and neuron parameters from conditions |
|
C8 |
connections by axon guidance |
|
C9 |
explicit cell positions and synapses (supervised) |
|
C10, C12 |
remove weak synapses, scale weights, shorten delays |
|
C11 |
simulate inside development and apply a plasticity rule |
|
A5 |
the original |
Every stage’s parameters, defaults and limits: Stage, geometry and field reference (also through the bridge stages method).
Legacy plans as schedules
Any BodyPlan can be compiled into a Project whose schedule reproduces grow() bit for bit:
from neurogenesis.development.legacy_compiler import compile_plan_to_project
project = compile_plan_to_project(plan)
tissue = run_schedule(project, genome, seed=0, legacy_stream=True)
net = tissue.to_network()
legacy_stream=True exists only for legacy plans (one random stream shared by all stages, as in grow); new
schedules never use it.
PLACEHOLDER
A full worked example — a layered sheet grown from progenitors (seed → proliferate → migrate into layers → differentiate by layer → grow axons → refine by spontaneous waves → prune) — once the fix wave is merged (roadmap milestone M2, example G2.3), with a GIF of every stage.