Skip to content

Release 0.13 - #261

Merged
derrynknife merged 1 commit into
masterfrom
dev
Oct 9, 2026
Merged

derrynknife merged 1 commit into
masterfrom
dev

Conversation

@derrynknife

Copy link
Copy Markdown
Owner

Summary

The 0.13 release: #260, from dev into master. #259 already brought everything else.

#260 does three things:

  • Removes what 0.12 deprecated:
    • Calling SparesDemand.mean() or std() now raises TypeError; they have been properties since 0.12.
    • StandbyModel's and LoadSharingModel's mc_samples, lower and seed now raise TypeError.
    • StandbyModel's switching_probability and dormancy_factor are now given by name.
  • Sets the version to 0.13.
  • Rolls the CHANGELOG into [0.13] - 2026-10-09.

Type of change

  • Bug fix
  • New feature
  • Documentation
  • Refactor / tooling
  • Breaking change (the removals above; the CHANGELOG lists 0.13's behaviour changes)

Checklist

  • Tests added/updated (asserting against a reference value where possible)
  • black, isort and flake8 pass on the changed files
  • coverage run -m pytest passes and stays above the coverage gate: the full suite runs once, on master's merge commit, before the release
  • CHANGELOG.md updated: [0.13]
  • Any Monte-Carlo tests are seeded (deterministic)

Notes for reviewers

At the maintainer's request, this PR is merged without its own CI run. The full suite runs once, on master's merge commit. release.yml requires that run to pass before it publishes 0.13.

🤖 Generated with Claude Code

https://claude.ai/code/session_01NUGaeQXKxjjiQ1ULT1ApXS


Generated by Claude Code

… guide (#260)

* Refuse node_availability with crews and common-cause groups (#251)

With limited repair crews, every long-run value but node_availability
already refused common-cause groups. node_availability gave each
component's value in the crews' chain, which has no common causes,
although a shared failure leaves one member waiting for the crew. The
crews' chain now refuses groups itself (_require_crew_chain), so neither
it nor its route can be read without them. The refusal says to simulate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NUGaeQXKxjjiQ1ULT1ApXS

* Keep every level's structure up to date; no truth tables (#255)

The compiled engine built a table of whether the system works in each of
the 2^n states of its components (n <= 20), and one for each nested RBD,
on every run: 0.11 s for 20 components, over half of a 1,000-simulation
run. The incremental structure it used above 20 components is as fast as
the table at every size measured, and faster at 20, so it is now the one
way: _compiled._kept lays out every level's structure as one (each path
set's level, each level's root and whether it always works), and the
kernel keeps it up to date at every level (_keep, _level_works). The
tables, truth_table, _works and MAX_TABLED are gone, and with them the
refusal of a nested RBD of more than 20 components.

Results are the same, bit for bit. A 1,000-simulation run of two lines
of ten components takes 0.036 s rather than 0.119 s; 20,000, 0.62 s
rather than 0.70 s. engines.API is 3, as the run's arrays changed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NUGaeQXKxjjiQ1ULT1ApXS

* Work out each stream's key once a run (#254)

A run's Plan keeps each stream's Philox key, worked out once, and hands
it to the blocks: 40 SeedSequences rather than 800 for 20,000
simulations of 20 components (about 0.035 s of 0.65 s).

Laying the draws out more tightly was measured and left out: a limit for
each simulation, extended alone, with first chunks nearer the expected
draws, worked out a third fewer values but ran the simulations that ran
out again, for no net gain on any of five benchmarks. The quantile
functions' overhead is raised in surpyval (SurPyval#769).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NUGaeQXKxjjiQ1ULT1ApXS

* Capacity: add up equal levels with a sorted reduction (#246)

merged() summed the probabilities of equal levels with np.add.at, one
row at a time. A stable sort of the rows by level and np.add.reduceat
make the same sums in the same order (each level from its first row,
and 0.0 + x is x), so results are the same, bit for bit. tidy() is
cached: a run tidies the same few levels for every state it meets.

25 redundant pairs in series (50 components with capacities), 2,000 h,
numba: 500 simulations 0.39 s -> 0.31 s, 2,000 simulations 1.04 s ->
0.96 s; 70 pairs on the Python loop, 100 simulations, 3.83 s -> 3.5 s.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NUGaeQXKxjjiQ1ULT1ApXS

* Timelines: work out each change's position once (#247)

A _Data's changes' histories and positions in them are a cached
property, worked out once with a repeat of the offsets (no gather
through the histories' numbers), where every measure (uptime, failures,
the curve, the merges) worked them out again. A unit's own histories no
longer store their changes' index among their cause's changes, which is
their position (index None; _Data.indices gives either).

Results are the same, bit for bit. The 12-component bridge feeding a
vote, 20,000 simulations, numba: simulate_timelines 0.76 s -> 0.69 s,
the first measures over its result 0.79 s -> 0.68 s.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NUGaeQXKxjjiQ1ULT1ApXS

* Conditional runs: order the modules' changes in one sort (#248)

paths() put every module's changes in order with a four-key lexsort, and
the stretches with another. The changes now take one sort of a key that
grows with the simulation and the time (each simulation's times shifted
past the one before's by a power of two beyond the window, so rounding
never puts two out of order), and each run of equal keys (changes at one
instant, or within rounding) is put in order exactly: by simulation,
time, rank, then as given. The stretches need no sort: a simulation's
start goes before its first change, the changes already in order.
values() and capacity_values() find each joint state's stretches, and
the changes down from and into it, from one stable sort, rather than
comparing every stretch with every state.

Results are the same, bit for bit. Also: the simulation guide said the
compiled engine takes nested RBDs of up to 20 components, which #255
lifted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NUGaeQXKxjjiQ1ULT1ApXS

* CHANGELOG: faster capacity runs, timelines and conditional runs (#246, #247, #248)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NUGaeQXKxjjiQ1ULT1ApXS

* Capacity: black (#246)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NUGaeQXKxjjiQ1ULT1ApXS

* Capacity: sum no levels when none is given; equal to np.add.at to rounding (#246)

The sorted reduction marked a first row even with no rows, so a run
whose capacity had no level left failed in add.reduceat. Its sums also
differ from np.add.at's in the last bits where a level has three or more
rows (reduceat adds a level's later rows up before its first): the test
now checks the sums to rounding and the levels exactly, and the
CHANGELOG says so.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NUGaeQXKxjjiQ1ULT1ApXS

* Release 0.13: remove what 0.12 deprecated; version, CHANGELOG, saving guide

What 0.12 deprecated goes: calling SparesDemand.mean() or std(),
properties since 0.12 (#184), raises TypeError, as do StandbyModel's and
LoadSharingModel's mc_samples, lower and seed, which set a fit to
simulated lifetimes that 0.12 removed (#149). StandbyModel's
switching_probability and dormancy_factor came after them, so they are
given by name, and a call that gave them by position is refused rather
than misread. test_removed_in_0_13 keeps them gone.

deprecation.NEXT_REMOVAL is now 0.14, what 0.13 deprecates (offsets=,
CapacityDistribution.mean() called, an exact mean's simulation options),
and REMOVAL_AFTER_NEXT goes. The CHANGELOG's [Unreleased] becomes
[0.13] - 2026-10-09, with its summary and behaviour changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NUGaeQXKxjjiQ1ULT1ApXS

---------

Co-authored-by: Claude <noreply@anthropic.com>
@derrynknife
derrynknife merged commit debbb6f into master Oct 9, 2026
2 of 20 checks passed
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