Skip to content

Add a notebook on hierarchical (tuple-valued) states - #20

Merged
wsdewitt merged 7 commits into
mainfrom
cell-cycle-notebook
Oct 18, 2026
Merged

wsdewitt merged 7 commits into
mainfrom
cell-cycle-notebook

Conversation

@wsdewitt

@wsdewitt wsdewitt commented Oct 18, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Adds a tutorial notebook, docs/notebooks/hierarchical_states.ipynb, listed in the docs between the quickstart and performance notebooks. It shows how to use a tuple as a node state, which BDMS supports since 1.0.1 (#19).

The model

Each cell's state is (cell_type, k), combining a differentiation hierarchy (progenitor → intermediate → terminal) with K cell-cycle stages:

  • Progression: a cell in stage k < K - 1 moves to stage k + 1 at rate K / T_gen, as BDMS mutation events.
  • Division: only cells in the last stage divide, also at rate K / T_gen. A cycle is then K exponential stages, so generation times are gamma (Erlang) distributed with mean T_gen and standard deviation T_gen / sqrt(K), and K = 1 is an ordinary birth process.
  • Differentiation: with birth_mutation_prob=1.0, each daughter gets a mutation event at birth. The mutator's transition matrix sends the last stage to stage 0 of the daughter's cell type, chosen with fixed probabilities.
  • Death: each cell type has its own death rate, highest for intermediates. Terminal cells don't divide.

I used a finite birth rate in the last stage rather than an infinite one, so that all K stages have the same mean and K = 1 reduces to the exponential case.

What the notebook shows

  • Generation times: the gamma densities for K = 1, 10, and 100, overlaid on one plot in a light-to-dark gray ramp, with a legend and labels on the curves.
  • Simulation and tree: one tree simulated with K = 100 for 10 mean generation times, rendered with branches colored by cell type and a color key. Divisions happen in nearly synchronized waves.
  • Synchronization: division times pooled over 200 simulations. They cluster around multiples of T_gen and spread out over time.

The processes and mutator are built inline and passed to Tree.evolve directly, with no helper functions. The cell-type colors are the first three slots of a palette that passes colorblind-separation checks for every pair of colors, and blue isn't reused elsewhere in the notebook, so it only ever means progenitor.

nbval tag

In a fresh environment, the first matplotlib import prints "Matplotlib is building the font cache" to stderr, which nbval treats as an unexpected output. The performance notebook's first cell is tagged nbval-ignore-output for this reason. Notebooks run alphabetically, so this notebook now runs first, and its first cell has the same tag.

Testing

On Python 3.15, nbval passes on all three notebooks. On Python 3.13, hatch run tests passes in a fresh clone with an empty matplotlib cache, as in CI. The new notebook takes about 40 seconds, most of it the 200 simulations for the synchronization histogram. Ruff passes on the notebook, and the docs build without new warnings.

🤖 Generated with Claude Code

wsdewitt and others added 7 commits October 17, 2026 22:53
The notebook models cells with a (cell_type, k) state: a differentiation
hierarchy (progenitor -> intermediate -> terminal) combined with K
cell-cycle stages. Cells divide only in the last stage, which gives
gamma-distributed generation times with mean T_gen and standard
deviation T_gen / sqrt(K). Intermediate cells have the highest death
rate. The notebook renders trees colored by cell type for K = 1, 4, and
32, and shows that divisions stay synchronized for large K.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In a fresh environment, the first matplotlib import prints "Matplotlib
is building the font cache" to stderr, which nbval reports as an
unexpected output of the first notebook cell that imports matplotlib.
This failed CI once hierarchical_states.ipynb became the first notebook
to run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…the font cache

The performance notebook already tags its first cell with
nbval-ignore-output, so that the message matplotlib prints while building
its font cache doesn't fail nbval. Tag the first cell of
hierarchical_states.ipynb the same way, now that it runs first, and
remove the extra step in the tests script.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Also includes two wording edits made in the notebook directly: the intro
no longer mentions the required BDMS version, and the text before
cell_cycle_model describes what the function builds and why it's a
function. The sentence about dashed branches now cites the death rate,
since intermediates aren't the majority of deaths in every tree.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Runs the single-tree simulations for 10 mean generation times instead
of 8, with the tree renders re-executed, and removes the closing note
about other combinations of attributes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Simulate only with K = 100, building the processes and mutator inline
and passing them to evolve directly, without the cell_cycle_model and
render helper functions. The generation-time densities for K = 1, 10,
and 100 are now overlaid on one plot, labeled directly. The tree render
and the synchronization histogram are for K = 100 only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The generation-time plot now shows K = 1, 10, 100, and 1000 with the
default matplotlib colors and a legend, and the synchronization
histogram uses bins of T_gen / 15. Reference lines are black. The
notebook was re-run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@wsdewitt
wsdewitt merged commit cc3f76d into main Oct 18, 2026
5 checks passed
@wsdewitt
wsdewitt deleted the cell-cycle-notebook branch October 18, 2026 07:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant