Add a weighted mode for asymmetric division - #68
Merged
Merged
Conversation
asymmetric_division_function entered its renormalization block on total > 1.0 + tolerance, so raising the tolerance did not only relax when an overshoot is an error -- it also decided whether the probabilities were rewritten at all. That block sets the symmetric division probability, so gating it on a user-settable threshold let the tolerance change the model rather than its strictness. With a tolerance of 0.5 and probabilities of 0.3 for (stem, stem) and 1.0 for (stem, progenitor_1), the total of 1.3 fell inside the old gate, so nothing was adjusted and the draw ran against a distribution summing to 1.3. Because select_daughter_types draws from [0,1), (stem, stem) kept its 0.3 instead of being taken down to 0, and progenitor_1 was starved to 0.7. Running the asymmetric division sample that way ends with 464 cells, 137 of them stem; adjusting whenever the total passes 1 ends with 16 cells and a stem population that stays at 1, which is what those probabilities describe. Adjust on total > 1.0 and leave the tolerance its one job: deciding whether the resulting symmetric division probability is negative enough to be an error rather than round-off. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The extended asymmetric division values are probabilities: they must sum to at most 1, and whatever they leave short of 1 is the chance of dividing symmetrically. That constraint is awkward for the models this feature is aimed at. The intended use is rule-driven values, and a Hypothesis Grammar rule cannot see what the other daughter-type pairs currently evaluate to, so keeping a sum at or below 1 is not something a rule set can enforce. The configurable tolerance buys slack for round-off, but cannot rescue a model whose values legitimately sum to 2 or 3. Adds <asymmetric_division_mode> to the <options> block, accepting "probabilities" (the default, and what an absent element means) or "weights". Under weights the values are relative weights, normalized by their own sum at each division, so their scale stops mattering and each rule can be written on its own. A string rather than a bool so a typo errors out instead of quietly reading as false and running the other model. The mode is model-wide rather than per cell definition: mixing them would make one rule line mean a probability for one source type and a weight for another, with nothing in the line to say which. Weights carry no implicit symmetric-division remainder -- symmetric division needs its own (type,type) weight -- except that an all-zero total, which has no normalized distribution, is defined as symmetric division. That needs no special case: leaving the draw scale at 1.0 makes select_daughter_types fall through to the parent and daughter types unchanged. The draw itself is one line. select_daughter_types takes a total_weight that scales the random draw; probability mode passes 1.0, which is exact in double precision, so no existing model's RNG stream moves. Confirmed by running the asymmetric division sample before and after: 487 .mat files byte-identical, and 244 SVG and 243 XML identical once wall-clock timestamps are stripped. Setting <asymmetric_division_probability_tolerance> together with weights is rejected at parse time. The tolerance bounds how far probabilities may sum past 1, and weights are normalized rather than bounded, so there is nothing for it to do; erroring beats letting one of the two silently win. An explicit "probabilities" mode alongside a tolerance stays legal, since that is unambiguous. The extended_asym_div sample gains a second config and rules file expressing the same model in weights, plus a README covering both modes and the trap that separates them: probabilities take the whole overshoot out of the symmetric entry, while weights scale every entry proportionally. The weights rules saturate at 1.0 where the probabilities rules use 0.5, chosen so the normalized weights reproduce the probabilities exactly. Verified single-threaded: across all 121 snapshots the two runs agree on every cell, and the only recorded difference is the asymmetric division values. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
drbergman
force-pushed
the
claude/asym-div-weights-my-physicell
branch
from
August 21, 2026 12:49
c0ddb65 to
e76a968
Compare
drbergman
marked this pull request as ready for review
August 21, 2026 13:49
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.
The
my-physicellcounterpart of #67, which targetsfeature-extended-asym-div. Two commits, reviewable in order.my-physicellalready carries theresume_from_MultiCellDSfix that the other PR needs, so only these two land here. The resume block was taken from this branch verbatim over there, so the two trees agree on it.1. Adjust asymmetric division probabilities whenever they sum past 1
asymmetric_division_functionentered its renormalization block ontotal > 1.0 + tolerance. That block rewrites the symmetric division probability, so gating it on a user-settable threshold let the tolerance change the model, not just its strictness.With
tolerance = 0.5and probabilities of 0.3 for(stem, stem)and 1.0 for(stem, progenitor_1), the total of 1.3 fell inside the old gate, so nothing was adjusted and the draw ran against a distribution summing to 1.3. Sinceselect_daughter_typesdraws from[0,1),(stem, stem)kept its 0.3 instead of going to 0, andprogenitor_1was starved to 0.7:total > 1.0 + tolerancetotal > 1.0The tolerance now has one job: deciding whether the resulting symmetric probability is negative enough to be an error rather than round-off.
2. Add a weighted mode for asymmetric division
The values are probabilities: they must sum to at most 1, and the shortfall is the chance of symmetric division. That is awkward for the models this feature targets. The intended use is rule-driven values, and a Hypothesis Grammar rule cannot see what the other pairs currently evaluate to, so no rule set can hold a sum at or below 1. The tolerance buys slack for round-off; it cannot rescue a model whose values legitimately sum to 2 or 3.
<asymmetric_division_mode>in<options>takesprobabilities(the default, and what an absent element means) orweights. Under weights the values are relative weights normalized by their own sum at each division, so scale stops mattering and each rule can be written independently.Details worth a reviewer's attention:
xml_get_bool_valuereads a typo asfalseand silently runs the other model. A bad value here exits.(type,type)weight. The exception is an all-zero total, which has no normalized distribution and is defined as symmetric division — no special case needed, since leaving the draw scale at 1.0 makesselect_daughter_typesfall through to the parent and daughter types unchanged.<asymmetric_division_probability_tolerance>, rejected at parse time. The tolerance bounds how far probabilities may sum past 1; weights are normalized rather than bounded. An explicitprobabilitiesmode alongside a tolerance stays legal, since that is unambiguous.The draw itself is one line —
select_daughter_typestakes atotal_weightscaling the random draw. Probability mode passes 1.0, which is exact in double precision, so no existing model's RNG stream moves.Verification
.matbyte-identical, 244 SVG and 243 XML identical once wall-clock timestamps are stripped.1.0/0.5/0.5→ 50/25/25,3/1→ 75/25,7/2/1→ 70/20/10, and0.07/0.02/0.01→ the same 70/20/10, confirming scale-invariance. Probability mode still leaves the shortfall to symmetric division.probabilities+ tolerance runs.Sample
extended_asym_divgainsPhysiCell_settings_weights.xml,cell_rules_weights.csv, and a README. The weights rules saturate at 1.0 where the probabilities rules use 0.5, chosen so the normalized weights reproduce the probabilities exactly — the two configs are the same model written two ways. Verified single-threaded: across all 121 snapshots the two runs agree on every cell, and the only recorded difference is the asymmetric division values themselves ((type0,type0)reads 0.5 in one and 1.0 in the other).That pairing is deliberate, because the two modes renormalize differently and the difference is not subtle: probabilities take the whole overshoot out of the symmetric entry, while weights scale every entry proportionally. Feeding the probabilities config to weights mode unchanged gives
(type0,type0)1.0/1.5 = 2/3 of divisions instead of 1/2. Same numbers, different model — which is what the mutual-exclusion error exists to prevent.One thing noticed in passing and left alone: with
<omp_num_threads>above 1 this sample is not bit-reproducible from the seed, and becausetype0sits near a critical branching process the run-to-run spread is large (44 vs 242 final cells across two runs of the same config). That is pre-existing and unrelated; the README notes it.🤖 Generated with Claude Code