Repository navigation
Handle zero total rates in capacity methods, and document capacity in the quickstart - #22
Merged
Merged
Conversation
With capacity_method="birth" or "death", evolve divides by the total birth or death rate of the active nodes, and Process.waiting_time_rv divides by the rate multiplier. Either can be zero, for example when no active node is in a state that can divide, or with a zero death rate. These divisions raised ZeroDivisionError. In 0.6.2 some of these cases ran by accident, through numpy's division-by-zero semantics, until ConstantProcess started returning Python floats in 1.0.0. - evolve updates a rate multiplier only when the total it divides by is positive. Otherwise every active node has rate zero for that event, so the multiplier doesn't matter. - waiting_time_rv treats a zero rate multiplier as an infinite waiting time. Every case that ran in 0.6.2 gives the same trees, and seeded results are unchanged. Also adds tests for each capacity method and adds "stop" to the type annotation of capacity_method. Closes #21 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Explains capacity and each capacity_method, and plots the population size over time for "stop", "hard", "birth", and "death", using Tree.slice to count the lineages alive at each time. The default (capacity_method=None) is shown raising an error. The quickstart now imports matplotlib, so its first code cell is tagged nbval-ignore-output, like the other notebooks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #21.
Summary
capacity_method="birth"or"death",evolvedivides by the active nodes' total birth or death rate, andProcess.waiting_time_rvdivides by the rate multiplier. Either can be zero, which raisedZeroDivisionError:evolvenow updates a rate multiplier only when the total it divides by is positive. When that total is zero, every active node has rate zero for that event, so the multiplier doesn't matter.waiting_time_rvtreats a zero rate multiplier as an infinite waiting time. It still draws from the random number generator, so the random stream is used the same way as before."stop"is added to the type annotation ofcapacity_method, which left it out.capacityand eachcapacity_method, shows the default raising an error when the population exceeds the capacity, and plots the population size over time for"stop","hard","birth", and"death". The population sizes come fromTree.slice. The quickstart now imports matplotlib, so its first code cell is taggednbval-ignore-output, like the other notebooks.Regression
This was partly a regression in 1.0.0. In 0.6.2, zero totals caused a numpy division by zero when they came from a
ConstantProcess, which gaveinfwith a warning, so the simulation happened to continue. Since 1.0.0,ConstantProcessreturns a Python float, so those cases raised. Totals from aDiscreteProcesswere Python floats in 0.6.2 too, so those cases also raised before 1.0.0.Results don't change
I ran each capacity method on 0.6.2 and on this branch, for 10 seeds each, with Python 3.12. The trees are identical for all seven configurations:
None(capacity exceeded),"stop","hard","birth","death", and"birth"and"death"with a zero death rate. The cell-cycle model from #21 now runs with either kind of death process, without warnings. The hash over about 140 other seeded simulations is unchanged.Tests
TestCapacityadds six tests:test_exceeded: with no capacity method, exceeding the capacity raisesTreeErrorand removes the partial simulation.test_stop:"stop"ends early, with exactlycapacitysurvivors at a single end time.test_hard:"hard"ends with at mostcapacitysurvivors.test_birth_and_death:"birth"and"death"keep the population between half and twice the capacity.test_zero_total_rate: the cell-cycle model from Capacity methods "birth" and "death" raise ZeroDivisionError when a total rate is zero #21 runs withcapacity_method="birth", with aConstantProcessor aDiscreteProcessdeath rate.test_zero_rate_multiplier: a zero rate multiplier gives an infinite waiting time.The last two tests fail without this fix, and all of them pass with it. On Python 3.15, the unit tests, doctests, and all three notebooks (nbval, including from an empty matplotlib cache) pass, along with ruff, docformatter, and pyright. Re-running the quickstart left every existing cell's output unchanged, and the docs build.
🤖 Generated with Claude Code