Skip to content

✨ Add optional mortality to calc_capacity - #421

Merged
clorton merged 3 commits into
mainfrom
capacity-with-cdr
Jun 29, 2026
Merged

✨ Add optional mortality to calc_capacity#421
clorton merged 3 commits into
mainfrom
capacity-with-cdr

Conversation

@clorton

@clorton clorton commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator

calc_capacity() gains keyword-only parameters deathrates and mortality_safety_factor to compute a peak-living bound instead of a cumulative-births bound. When mortality is provided, the function credits
a configurable fraction of projected deaths against births, with the remainder held back as headroom. Per-node estimates floor at initial_pop
for net-shrinking projections. Math mirrors calc_capacity_cdr from razer.

@clorton clorton added this to the M3: LASER 1.1 milestone Jun 23, 2026
@clorton
clorton requested review from Copilot and jonathanhhb June 23, 2026 02:47
@clorton clorton self-assigned this Jun 23, 2026
@clorton clorton added the enhancement New feature or request label Jun 23, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extends laser.core.utils.calc_capacity() with optional mortality inputs so capacity sizing can target a peak-living (slots-reusable) bound rather than a births-only bound, and adds tests + changelog documentation for the new API.

Changes:

  • Added keyword-only deathrates and mortality_safety_factor parameters to calc_capacity() with validation and updated docstring semantics.
  • Implemented mortality crediting (attenuating growth by a configurable fraction of projected deaths) and an initial_pop floor for net-shrinking projections.
  • Added a comprehensive test block covering backward compatibility, monotonicity w.r.t. mortality_safety_factor, validation, and keyword-only enforcement; updated CHANGELOG.rst.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
src/laser/core/utils.py Adds mortality-aware capacity sizing parameters, validation, and estimation logic in calc_capacity().
tests/test_utils.py Adds unit tests for the new mortality feature and API surface behavior.
CHANGELOG.rst Documents the new calc_capacity() mortality capability and related semantics.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/laser/core/utils.py
Comment thread tests/test_utils.py
@clorton clorton linked an issue Jun 23, 2026 that may be closed by this pull request
@clorton
clorton force-pushed the capacity-with-cdr branch 2 times, most recently from 8cff9b6 to 6a912f1 Compare June 25, 2026 20:47
jonathanhhb
jonathanhhb previously approved these changes Jun 27, 2026

@jonathanhhb jonathanhhb left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Walked through the math and ran a small mutation-test pass against the 8 new tests — introduced 9 plausible bugs in calc_capacity (sign flips, end-of-sim vs peak-across-time, dropped validation, removed * keyword-only marker, typo lamda_d from birthrates instead of deathrates, etc.). 7 of 9 were killed by at least one test, with peak_living_exceeds_end_of_sim_for_fluctuating_rates uniquely catching the most important one (using end-of-sim cumulative sums instead of the peak). The hand-computed peak for that test lands at ≈1.245M, well inside the [1.20M, 1.30M] band.

One optional ask before merge — the two floors at initial_pop (the inner np.maximum(np.max(cumsum, axis=0), 0.0) capping peak_exponent at 0, and the outer np.maximum(estimates, initial_pop)) are belt-and-suspenders: removing either one in isolation still passes every test, including test_calc_capacity_net_shrinking_floors_at_initial_pop, because in every net-shrinking scenario the surviving floor produces the same answer. Not a correctness bug — both genuinely prevent under-allocation — but worth either deduplicating, or adding a one-line comment noting they're intentionally redundant so a future refactor doesn't quietly strip a safety layer.

@jonathanhhb jonathanhhb left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See previous comment.

clorton added 3 commits June 27, 2026 09:01
`calc_capacity()` gains keyword-only parameters `deathrates` and
`mortality_safety_factor` to compute a peak-living bound instead of a
cumulative-births bound. When mortality is provided, the function
credits
a configurable fraction of projected deaths against births, with the
remainder held back as headroom. Per-node estimates floor at
`initial_pop`
for net-shrinking projections. Math mirrors `calc_capacity_cdr` from
razer.
Fluctuating CBR/CDR rates can produce a population peak well above the
end-of-simulation value. The previous formula used the end-of-sim
cumulative exponent, which would under-allocate the LaserFrame for
scenarios with intermediate spikes.

Now computes max_t cumsum(λ_b - death_credit × λ_d), floored at 0, and
uses that as the exponent. Monotonic-growth scenarios (births dominate
every tick) remain unchanged because peak == end when cumulative is
monotonic.

Includes two regression tests (spike-then-decline and monotonic case)
and
an illustrative script with plot under
misc/calc_capacity_peak_vs_end.{py,png}.

Also fix typo: "death sum" → "death rate" in the docstring.
The inner and outer floors in calc_capacity serve different
purposes. Updated comments to explain their distinct roles
and why both are intentionally kept despite redundancy.
@clorton
clorton force-pushed the capacity-with-cdr branch from 6a912f1 to 89da321 Compare June 29, 2026 23:10
@clorton
clorton merged commit 705f1e3 into main Jun 29, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add CDR to calc_capacity for long running simulations

3 participants