Repository navigation
Merge dev into master: the work since 0.12 - #259
Merged
Merged
Conversation
…nctions The input and output nodes are inferred from the edges and never fail, so a model given for one was silently dropped. Forgetting a component's edge to the output node made that component the output node, and the answer changed with no warning (#217). The base RBD's structure check now reports such a model as an invalid structure, naming the edge most likely missing, on both RBD classes. PerfectReliability, or a fixed probability of failing of 0 (as node_availability()'s 1.0 for each end gives), may still be given for an end, and on_infeasible_rbd="warn" keeps the old behaviour. NonRepairableRBD.node_mttf() leaves junctions out, as the importance measures do, where it raised on PerfectReliability's missing mean though analysis_routes() called it exact (#228). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NUGaeQXKxjjiQ1ULT1ApXS
) The routes test now calls every analysis the report lists (27 were never called) on one catalogue of diagram kinds (tests/catalogue.py), shared with the engines' agreement test and with new checks of the exact and numerical routes against the simulation of the same quantity. A guard fails when a node class, spec key, policy or diagram option has no diagram, naming it. It found the report calling exact or simulated what the method refuses, now refused in the report with the method's own message: NonRepairableRBD's parameter-uncertainty methods on a node with no reliability or a structure too meshed; minimum_effort_allocation off a series system (now found from the graph, a pass per node); a system_capacity of capacities taken from models; and a repairable availability allocation with no component to allocate. Also: a fixed-probability node's simulated lifetime is 0 or inf, as sf takes it (random() read surpyval's 0/1 indicator as a time), and an MTTF estimate refuses a diagram of fixed probabilities alone; node_mttf() gives a PerfectUnreliability node 0 (both helpers gain mean()); and a MixtureModel node no longer breaks analysis_routes() or saving. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NUGaeQXKxjjiQ1ULT1ApXS
A diagram simulated from several threads at once crashed (AttributeError '_cancelled'/'_crews') or returned another seed's result: the event loop keeps a run's state on the diagram, and draws that cannot be streamed come from numpy's global RNG, which a seeded run seeds and restores. A process-wide re-entrant lock now makes the runs take turns, each giving what it gives alone: RepairableRBD._run (but a sharded run's parent, so a shard_map on threads cannot deadlock), numpy_seed and the timelines' event-loop re-runs hold it. The exact methods, n_jobs' processes and the compiled engine's threads within a run are not held up. test_threads.py checks seeded calls on threads against their serial results (it fails without the lock). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NUGaeQXKxjjiQ1ULT1ApXS
…rtainty (#215) When the dependent modules stayed in the state they started in, in every simulation (a standby pair that never went down in the window), each simulation's expected values given them were the same: the run reported a standard error of 0 and met a tolerance after its first batch, for the value of the rest of the system alone. Such a run now warns; a default run's mean intervals are its simulations' own (method="simulated") and judge its tolerance; a run of the modules alone has no error to give (nan) and runs on to max_samples. A run with no modules stays exact. ConfidenceInterval.method now names every case of a repairable run's mean (#223): "simulated", "control_variate", "conditional" or "exact". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NUGaeQXKxjjiQ1ULT1ApXS
In a RepairableRBD, nodes holding the same fitted life but repair fits of
their own had their life drawn per node by default, and no spec could
share it alone, so the *_uncertainty intervals were about 40% too narrow
and the vega split wrong. Draws are now shared per fitted object in each
role: a node may come under several inputs, a role of it under one
({("a", "b"): {"reliability": "fit"}, "a": {"repairability": "fit"}}),
which the default now gives, ordered by first node and role. In both
diagram classes, a spec that draws one node and leaves others holding
the same fitted object as fitted is warned about.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NUGaeQXKxjjiQ1ULT1ApXS
…221, #222) optimal_inspection_intervals no longer refuses a component whose tests can miss a failure (coverage below 1), which 0.12 listed as numerical: it keeps its full tests' interval, and its test interval is chosen among those that divide it, from allowed (one that does not is refused, by name, with some that do) or, left out, from every one in range (a spread of divisors, then every one around the best). Its offsets= are shares of the interval, where with_intervals' and the plan's offsets are times, so a share passed to with_intervals undid a stagger silently: it is renamed offset_shares=, offsets= kept with a FutureWarning until 0.14 (deprecation.renamed, REMOVAL_AFTER_NEXT). An offset for a component with no tests is refused also when its interval is given; allowed= and offset_shares= dicts naming a node not chosen are refused; a node that is not a component is refused listing them; and an inspection plan always has its offsets. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NUGaeQXKxjjiQ1ULT1ApXS
A cost result's mean is its run's estimate of the expected cost, the one mean_interval gives: exact where the exact methods work it out, given the modules' histories where those apply, else the simulations' own. mean_se, cost_rate and the breakdowns follow it (the breakdowns exact or conditional too, from expected_cost or the module run); sample_mean and sample_se are the simulations' own. AvailabilityResult gains mean_availability (the interval's estimate) and sample_mean_availability. Both results' reprs are summaries. The docs' examples are checked for the True/False, string, tuple and list outputs they quote; the costs guide's interval check, which printed False, and a stale stepped-simulation time are fixed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NUGaeQXKxjjiQ1ULT1ApXS
- Grid of 500 steps over a component's typical up time (was 2,000). - A curve that has not settled grows to where its distance from its long-run value, falling cycle by cycle, says it will have settled, rather than four times as far. - Identical plain components (the same life and repair models, the same state at 0) share one curve. - unit_curve's convolutions transform each series once (_Spectra), and the series inverse uses a lean FFT product. Tests whose tolerances were set for the 2,000-step grid, the docs' quoted numbers and the CHANGELOG are still to be updated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015CpLNEXQaaMvAeEngrKJFC
…219) A FaultTree's or NonRepairableRBD's exact values conditioned on every combination of every group's shock outcomes, doubling the time per group. Each group is now conditioned on only within the smallest module holding its members; where a module's groups would multiply past 64 outcomes, their causes are written out as shock events (repeated nodes of the diagram, repeated events of the tree) for the decision diagram to work out. An MGL model's exclusive shocks go in as the independent causes that fail the same sets as often, where those exist. The values are those of every combination, which the new tests check against. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NUGaeQXKxjjiQ1ULT1ApXS
…rves 1,000 steps over a component's typical up time rather than the work in progress's 500: the exact step is still 5-6 times as fast as before (the settling, the shared curves and the transforms do the rest), with the error four times what it was at 2,000 rather than sixteen, so that event counts near fixed-time maintenance and point values soon after the start keep to a few parts in a million. Tests set to the 2,000-step grid's error are set to four times it, the error falling as the square of the step; own_rate's differences span a step of the coarser grid; the twin's curves are of three different units, now that identical ones share a curve. New tests: identical components share one curve, which gives what a curve each gives, and a curve is followed about as far as it takes to settle. The greeks guide quotes the valve's rate just after its block replacement as computed now (it converges slowly as the grid is refined, as it did before). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015CpLNEXQaaMvAeEngrKJFC
…urves Tests of event counts and point values against closed forms, and of a flat diagram against its nested form, are set to four times their error at 2,000 steps. CLAUDE.md: identical components share one curve, so anything that makes a unit's curve depend on more than its two models and its state at 0 must leave it out in _plain_unit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015CpLNEXQaaMvAeEngrKJFC
…epairs that take time (#218, #220) #218: the long-run values, importance measures, failure frequency and values over time split each time by every combination of every group's states, so memory doubled per group and ten groups were killed by the OS. Each group is now conditioned on only within the smallest module holding its members (_ccf_modules.Tabled), an owner's combinations laid out as a last array axis and worked out a bounded chunk at a time: fifty groups in series take a third of a second in flat memory, and groups that meet in one module are faster and leaner too. Too many combinations in one module are refused before anything is built; the capacity distribution and the allocations, which still split, refuse where that would take too much memory. Limited repair crews no longer drop the groups from the long-run values: they refuse, as over time. #220: a tested member's tests and repairs may take a fixed or an exponential time. The group's chain gains levels for a member off line for a test, under a test while failed, and under repair, as the simulation has them; the issue's 1oo2 with an eight-hour MRT gives 9.591e-4 against IEC 61508-6's 9.609e-4. What the chain does not take is refused saying what to do, without doubled apostrophes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NUGaeQXKxjjiQ1ULT1ApXS
A component's rate was its whole curve differenced over its grid's step, which smoothed over a down time far shorter than a step: the greeks guide's valve, maintained for about 6 h on a grid of days, had its rate a day after its replacement 1-2% low. _rates.derivative now takes a curve's own rate where it has one: a GridCurve differences its grid and adds its dips' rates, each from its down time's density differenced on its own scale (_density), and its ChainDips' from their interpolation on their fine grids (_cubic_slope); a BlockCurve the return from the block replacement before it; ShiftedCurve and StartedBlockCurve pass their curves' on; a nested RBD's SystemCurve takes its nodes' rates times their importance. The guides' quoted rates change to the corrected values. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015CpLNEXQaaMvAeEngrKJFC
Some curves already have a rate: a tested unit's constant failure rate. _rates.derivative takes a curve's derivative where it is callable. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015CpLNEXQaaMvAeEngrKJFC
#224, #225, #226, #227) - A RepairableRBD's importance measures and parameter_sensitivity take their times first, as a NonRepairableRBD's do: birnbaum_importance(5.0) is birnbaum_importance(x=5.0), barlow_proschan_importance(100.0) its window (#224). - A bare string given to working_nodes, broken_nodes or nodes is one node, on every method of RBD and both diagram classes, as is FaultTree.occurs("ab")'s event; unknown nodes are listed in order (#225). - The uncertainty methods take arrays of targets as the point methods do, a row of draws per target from the same parameter draws, and name the argument out of range; a life that may never end has infinite bounds, without numpy's warning (#226). - A surpyval MixtureModel is a RepairableRBD life (#227): the analyses take its sf, ff, df and mean, the streams draw it by a bracketed Newton inversion of its distribution (no qf, surpyval #651; the components' sf summed for the tail, surpyval #671), and imperfect repair and starts from an age through MixtureLife (surpyval #626). The uncertainty methods say a mixture has no parameter covariance; two mixtures are the same model only with the same weights; any other life is refused naming the component. Four mixture diagrams join the routes catalogue. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NUGaeQXKxjjiQ1ULT1ApXS
Build the exact curves several times as fast
availability_rate: differentiate the curves' dips exactly (#240)
surpyval's development branch no longer takes p, 0.22's name for a limited-failure population's share that ever fails, which 0.22 knows by no other: the building guide's example could not be written for both. 0.23 names it lfp_p, pickles its fits (SurPyval#573) and takes success_run's level as alpha_ci (SurPyval#580), so with it as the minimum: models are read by lfp_p alone (lfp_extras and the two names in _PLAIN go; a file saved before 0.10 with p in its extras still loads), a run of fitted models goes to n_jobs' workers as it is (_ModelPickler goes; dumps keeps its message for what cannot be sent), the tests pass alpha_ci and lfp_p directly, and NonRepairable.mean_availability's example runs. CLAUDE.md's list of surpyval workarounds is empty. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015CpLNEXQaaMvAeEngrKJFC
…r FV, faster interval searches (#229) - An MTTF integrates the survival function on a few pieces a decade from the models' kinks (a numerical curve's grid, where a support starts), each by the Gauss-Kronrod (7, 15) rule, and follows the tail only until it is 0: 513 evaluations where 20 different Weibulls in series took 63,100, to the same value. Network.mean() on a 10x10 grid takes 7 s where it took 209; the plan's replay takes more times at once. - A network refused as too meshed is refused at once after. - The decision diagrams' computed table starts again past 2^19 entries, and a cofactor leaves the nodes after its variable alone: Fussell-Vesely on a meshed core takes half the memory. - A common-cause group's chain keeps exp(G dt) for the steps it takes again; a plan keeps its groups' tables, and a search's plans share the tested units' models: a 2oo3 group's plan takes 0.2 s where it took 1.4 to 6.5, its choice of five intervals 32 s where it took 506. - Interval plans as good (within 1e-10) keep the first tried, so ties between identical members no longer turn on rounding. - A tested unit's walk convolves directly or by FFT itself. - CLAUDE.md: the MTTF's kinks, and the testing policy (targeted tests for dev, the full suite on request and before merging to main). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NUGaeQXKxjjiQ1ULT1ApXS
Require surpyval 0.23, and drop the workarounds for 0.22
… from new, cost API, exact compare and plain chunks (#230, #231, #234, #236) - optimal_replacement_intervals(allowed=...) chooses from calendar intervals, never replacing (inf) among them; 0.12's notes corrected (#230). - total_cost and expected_cost discount from new by parts on adaptive Gauss-Kronrod pieces; an endless horizon with a discount rate, many horizons at once, and a warning when the rate discounts the costs to almost nothing, as a rate per year with models in hours does (#231). - cost() and the run's cost result carry the acquisition cost when it is all that is priced; compare's cost counts it (#234). - compare is exact where both designs' expected values are (MTTFs, or mission availability and expected cost), simulated otherwise or on request (method="simulate", control_variate=False); its route says so, and a diagram too meshed to work out is left to the simulation (#236). - simulate_chunk and availability_from_chunks take control_variate=False and conditional=False, so chunks reproduce a plain run (#236). - Warnings point at the caller's own line (outside_level). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NUGaeQXKxjjiQ1ULT1ApXS
…t serialise, unavailability over time (#232, #233, #235, #237) #232: the names 0.12 removed are refused naming their replacement (refuse_removed_names), fussel_vesely names fussell_vesely; typos in node names, parts, trains, groups, roles and parameters get the closest name; a junction named as a component is called one; outage logs say which outage is wrong and how; gates in a loop say so; a single CCFGroup is a list of one; a PerfectReliability instance is the class (and a junction's spec without repairs saves); seeds are checked where given; spares_stock with crews says it has no simulation; warnings point outside the package (outside_level); no message reads "'a''s". #233: costs, intervals and times given as text or True/False are refused; surpyval's distribution itself is refused for a model of it; a repairable component given a life alone is told it needs its repairs; NonRepairable(life, "instant") replaces in no time; the exact mean of StandbyModel, LoadSharingModel and DegradingNode warns of the options it ignores (FutureWarning, 0.14 refuses) and takes method="simulate"; percentiles below 1 warn of their 0-100 scale; a part listing a component twice, or one in a part and nodes, is refused. #235: to_dict() on every result, AnalysisRoute, Pairs, Timeline(s), ready for json.dumps; summary reprs for SparesDemand, ControlVariate and ConditionalRun; CapacityDistribution.mean is a property (calling it warns until 0.14); an infinite risk reduction worth no longer warns. #237: an inspection offset within a billionth of the interval is no offset; FaultTree.from_rbd keeps a common-cause member the logic makes irrelevant; the staggered-tests convention is documented; new point_unavailability and mission_unavailability work out a small unavailability to its own precision. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NUGaeQXKxjjiQ1ULT1ApXS
A diagram's fault tree keeps a common-cause member its logic makes irrelevant, where the tests expected a refusal; gates in a loop say so; an outage that ends before it starts says that; a life alone given as a repairable component is told it needs its repairs; the advice for a mean with no exact value is mean(method='simulate', ...); and a component in both nodes and a part of spares_demand is refused. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NUGaeQXKxjjiQ1ULT1ApXS
…ads well (#238) The saving guide no longer says a simulated standby or load-sharing node is fitted to simulated lifetimes; the spares guide's mean and std are properties; a common-cause member fails with probability Q to first order, not exactly (common-cause guide, concepts, MGL); and the Raises sections of sf_given_state, remaining_life and mean_residual_life take a number as an age. Junctions, pooled spares, discounting, whole trains, plans that keep both risks, common causes in fault trees and the unavailability over time are in the README's list, the docs' home table and the guide's index; the glossary gains junction, the Greeks, differential and Barlow-Proschan importance, discount rate, fill rate, lead time, producer's and consumer's risk, and unavailability. help(): the package's docstring says where to start; the API reference's cross-references read as names at run time (utils.docs, applied to the exported API), the web pages keeping their links; the sensitivity measures name their Greeks; availability's longest options point to the guide. test_help.py checks it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NUGaeQXKxjjiQ1ULT1ApXS
…al 0.23 minimum) The CHANGELOG keeps both sides' entries; serialisation takes is_mixture and leaves lfp_extras, which surpyval 0.23 made unnecessary; CLAUDE.md's surpyval workarounds list what remains (the mixture's quantile). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NUGaeQXKxjjiQ1ULT1ApXS
…sults' properties and methods (#227, #235) test_performance_equivalence forced the slow path by patching repairable_rbd.inverse_sampler, which #227 replaced with stream_sampler (a mixture's quantile on top): it patches that now. The repairable guide says a result's values are properties and its intervals methods, and that to_dict() gives it as plain data. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NUGaeQXKxjjiQ1ULT1ApXS
A tested component with an exponential life, whose tests or repairs take time, follows its state from new until it is the long run's, which the chain took to be within 1e-15 of it. The iterated state settles a few roundings from the stationary one, how few depending on the platform's numerical libraries: on CI's Python 3.12 and 3.13 it never came that close, and its values over time past what the grid can follow were refused as not settled. The bound is now SETTLED (1e-12), as the walk's for any other life; a test nudges the long run by a few roundings. A refusal with no node names "A tested unit" rather than "Component None". Two tests that failed in CI after the merge of dev: - the discounted cost from new is checked to the precision of the 1,000-step availability curve it is worked out from (rel 1e-6); - the saving guide's engine comment no longer opens with a literal, which the docs test checked against the engine of a job without numba. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NUGaeQXKxjjiQ1ULT1ApXS
…endations-2lmh5a Fixes from the 0.12 persona check: correctness, API, messages, results, unavailability over time, docs (#213)
#244) parameter_sensitivity's levers had no public API: a report or an app that named them, showed their values or treated a calendar lever apart reached into repyability.rbd._sensitivity (levers, _calendar_lever, _as_spec). - RepairableRBD.levers() and NonRepairableRBD.levers() list what parameter_sensitivity moves, in the order it reports them, as Lever results: key and name, value, bounds (a model parameter's surpyval's, an option's as its constructor checks it), discrete, and calendar. - with_levers({lever: value}) builds the diagram with levers moved as the sensitivities move them, by Lever or (key, name). It refuses a lever the diagram does not have (with the closest name), a fraction of a count, text and booleans, and a value out of range (with the lever's range). - The private names callers used stay as they were for 0.13. - The non-repairable DIM's proportional changes take the levers' values, where _parameter_values listed the parameters by hand. - Docs: the greeks guide, the API page, the CHANGELOG and CLAUDE.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NUGaeQXKxjjiQ1ULT1ApXS
…#244) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NUGaeQXKxjjiQ1ULT1ApXS
surpyval's develop branch gives FixedEventProbability a quantile function, so inverse_sampler, which takes a model that samples by inverse transform with one global uniform a draw, now takes it there, and its draws are surpyval's own, to the last bit. The test that listed it among the models the sampler declines failed in the upstream job; it now holds the sampler to its contract on either surpyval: declined without a quantile function, and reproducing surpyval's draws with one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NUGaeQXKxjjiQ1ULT1ApXS
…endations-2lmh5a Public levers: levers(), Lever and with_levers on both diagram classes (#244)
The Python engine's second implementation of independent components' lives and repairs, which drew their histories from their streams, is gone: the event loop records them as it runs, the same histories bit for bit, and TimelineSimulation.method is always "event loop". Recording is cheaper (each component's changes kept together as they happen, planned changes by their places), and packaging the records into a block takes a third of its old time. A component that fails at once and is repaired at once is refused when the diagram is built: the loop would change its state without end. CLAUDE.md gains #204's rule for clean, simple implementations and loses the drawn histories' notes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NUGaeQXKxjjiQ1ULT1ApXS
The compiled loop wrote a component's events twice: once for the system's own components in _simulate, once for a nested RBD's in _advance, with a rule that a change to one goes into the other. One loop in _simulate now takes every level's events: the system's to the end, and a nested RBD's to its next change when that is wanted where it is a component, its level stacked on the one waiting for it. Each level starts when its node does. A component's first and next events, a standby group's step and the repair crews are each written once. Results are the same, bit for bit. A system with nested RBDs runs faster (a three-level system in about half the time), since a nested RBD's change no longer costs a call carrying the run's arrays; the others run as before. Sharing helpers called per event, or a function per simulation, cost 3 to 7 times the loop: numba copies and reference-counts every array such a call carries. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NUGaeQXKxjjiQ1ULT1ApXS
surpyval's development branch gives FixedEventProbability a quantile function (0 or inf) while its random() still draws the 0/1 event indicators its fit takes. The sampler takes the quantile function and draws lifetimes: the test now checks they are 0 exactly where surpyval's draw from the same uniform is the event, rather than equal to the indicators. It passes on surpyval 0.23 and develop. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NUGaeQXKxjjiQ1ULT1ApXS
The compiled radix sort and merge (_time_order) and their tuning constant go. The +1 and -1 changes of state are netted by one sort of integers, each time's bits above the change's sign; the capacity's changes, and the changes in how many systems can carry an unlimited amount, by one sort of (time, change) pairs. Results are the same, bit for bit; test_time_order checks each against its definition written out plainly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NUGaeQXKxjjiQ1ULT1ApXS
The rest of the change whose first commit removed _time_order: the +1 and -1 changes of state are netted by one sort of integers, each time's bits above the change's sign; the capacity's changes, and the changes in how many systems can carry an unlimited amount, by one sort of (time, change) pairs. Results are the same, bit for bit; test_time_order checks each against its definition written out plainly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NUGaeQXKxjjiQ1ULT1ApXS
bdd.search and shannon.replay/replay_gradient are each written once and run as Python or compiled by numba as written (_bdd_kernel), where numba is installed and a core is large. The compiled search's separate design, its packing of states into integers and its fall back to Python for a wide frontier go; the replay writes into room its caller gives (lists in Python, arrays reused across columns when compiled), with the reverse pass on the values the forward one left. Plans are the same step for step and values bit for bit: bdd_records.json holds the plans, step counts and replay values the old code made (and the new code re-records the file byte for byte), and test_bdd_records checks both modes against it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NUGaeQXKxjjiQ1ULT1ApXS
Uniform k of simulation r from a stream is now the k-th of numpy's Philox generator with the stream's key (from the run's entropy and the stream's name) and the counter (0, r, 0, 0): a function of those alone. _philox writes Philox4x64-10 once, run on numpy's arrays or compiled by numba a counter at a time (_philox_kernel); test_streams checks both against numpy's Philox, and that the layout (block widths, chunk rows) changes nothing a run gives. compare and the control variate no longer give two systems' streams the same widths, and shards() no longer passes them. This changes seeded results: seeded_event_loop.json is re-recorded, the docs' quoted numbers and the prose built on them are updated, and engines.API is 2. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NUGaeQXKxjjiQ1ULT1ApXS
--doctest-modules imports every module, and _philox_kernel imports numba: the plain CI jobs, which have no numba, failed to collect it. Ignore it there, as _kernel and _bdd_kernel are, in place of the removed _time_order, and leave it out of coverage likewise. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NUGaeQXKxjjiQ1ULT1ApXS
The docstrings' seeded examples quote the new streams' numbers. A simulated timelines test compared a history's change times as pairs: it held only while the first history had none, as the old streams gave it. A test that random prices add spread compared two runs' sample standard deviations, which chance can order either way: it now checks the spread the prices add to each simulation, with the failures the same. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NUGaeQXKxjjiQ1ULT1ApXS
test_runs_with_groups_are_reproducible_and_compiled patched _compiled.available to True for an analysis_routes check, then ran a compare under it. Since the streams' Philox runs compiled wherever numba is said to be available (#209), that compare tried to import numba on CI's jobs without it. The patch now covers the route check alone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NUGaeQXKxjjiQ1ULT1ApXS
…252) (#256) 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. Claude-Session: https://claude.ai/code/session_01NUGaeQXKxjjiQ1ULT1ApXS Co-authored-by: Claude <noreply@anthropic.com>
…257) * 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 --------- Co-authored-by: Claude <noreply@anthropic.com>
… (#258) * 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 --------- Co-authored-by: Claude <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.
Summary
Everything on dev since 0.12, into master. It is a merge, not a release: the version stays 0.12, and the CHANGELOG entries stay under
[Unreleased].What it brings (the CHANGELOG's
[Unreleased]section has the details):availability_rate(availability_rate is off by 1-2% just after a scheduled maintenance: the exact dips are differenced at the grid's step #240).Closes #204
Closes #205
Closes #206
Closes #207
Closes #208
Closes #209
Closes #213
Closes #214
Closes #215
Closes #216
Closes #217
Closes #218
Closes #219
Closes #220
Closes #221
Closes #222
Closes #223
Closes #224
Closes #225
Closes #226
Closes #227
Closes #228
Closes #229
Closes #230
Closes #231
Closes #232
Closes #233
Closes #234
Closes #235
Closes #236
Closes #237
Closes #238
Closes #239
Closes #240
Closes #244
Closes #246
Closes #247
Closes #248
Closes #251
Closes #252
Closes #253
Closes #255
Type of change
Checklist
black,isort,flake8, andmypypass (this PR's CI)coverage run -m pytestpasses and stays above the coverage gate: this PR's CI runs the full suite on Python 3.11–3.14, with numba, on the minimum surpyval and on surpyval's developCHANGELOG.mdupdated under[Unreleased]Notes for reviewers
docs.yml), so the published docs will describe features that are not on PyPI until the next release.🤖 Generated with Claude Code
https://claude.ai/code/session_01NUGaeQXKxjjiQ1ULT1ApXS
Generated by Claude Code