Sensitivities: the Greeks¶
An option's Greeks say how its price moves when something it depends on moves: delta for the price of what it is written on, gamma for how delta itself moves, theta for the passing of time, vega for the volatility, rho for the interest rate. The measures on this page ask the same questions of a system's availability or reliability: how far it moves when a component or a lever does, whether two improvements reinforce each other, what is moving it now, and whose uncertainty makes it uncertain. They are named after the Greeks so that the set reads as one family:
| Greek | What it asks | Method | Adds up |
|---|---|---|---|
| Delta | How far does the system move per unit of a component's availability? | birnbaum_importance |
|
| Parameter deltas | Which lever moves it most: a life, a repair, the maintenance, a crew? And per unit spent? | parameter_sensitivity |
|
| DIM | What share of a change lies in each component, or each group of levers? | differential_importance |
to 1 |
| Gamma | Are two improvements complements or substitutes? | joint_importance |
|
| Theta | Which component is moving the availability now? | availability_rate (reliability_rate) |
to the system's rate |
| Which component caused the system's failures? | barlow_proschan_importance |
to 1 | |
| Vega | Whose parameter uncertainty makes the answer uncertain? | uncertainty_importance |
to 1 (by the delta method) |
| Rho | How sensitive is a plan's value to the discount rate? | to come, with the plan's net present value |
Importance measures is the reference for each (and Reliability of a system for vega); this page runs one system through all of them.
What they share¶
- The same arguments. On a
RepairableRBDeach is a long-run value by default.xevaluates it at times from new,windowover[0, window), andstate=starts from the components' current states, aspoint_availabilityandmission_availabilitytake them (theta, a rate, needs its times; vega names its quantity,of=, and takes its times asx). On aNonRepairableRBDthey take the timex. On either, the times may come first, asbirnbaum_importance(5.0)(and Barlow–Proschan's window,barlow_proschan_importance(100.0)). All but vega takeworking_nodesandbroken_nodes, which hold nodes working or failed as for every importance measure. - Exact where the structure is. With independent components the
system's availability is multilinear in theirs, so delta, gamma, the
shares among components and theta's split are exact given the
components' values (which, over time, are worked out numerically on a
grid); a lever's derivative is a central difference of the system's own
value; vega is the delta method's linear approximation, or Sobol indices
estimated from draws. With limited repair crews or common-cause groups
the components are not independent, and the measures follow the crews'
and the groups' Markov chains instead, where they can (see the last
section).
analysis_routes()says, for a given diagram and without running anything, which of these is exact, numerical or refused, and why. - Shares that add up. The differential importance, theta's parts, Barlow–Proschan's shares and vega's (by the delta method) add up to the whole, so they can be shown as a breakdown. Delta, the parameter deltas and gamma rank; they do not add up.
One station, every measure¶
A pumping station: two pumps in parallel feed a valve. Times are in days. The pumps wear out over about 40 days and take a day to repair; the valve lasts longer, is repaired in half a day, and is replaced at age 80 days, a quarter of a day's work:
import surpyval as surv
from repyability import RepairableRBD
E, W = surv.Exponential.from_params, surv.Weibull.from_params
def unit(life, repair, **more):
return {"reliability": life, "repairability": repair, **more}
station = RepairableRBD(
[("s", "pump1"), ("s", "pump2"),
("pump1", "valve"), ("pump2", "valve"),
("valve", "t")],
{
"pump1": unit(W([40, 2.0]), E([1.0])),
"pump2": unit(W([40, 2.0]), E([1.0])),
"valve": unit(
W([160, 1.5]),
E([2.0]),
preventive={"policy": "age", "interval": 80.0, "duration": E([4.0])},
),
},
)
station.mean_availability() # -> 0.99463
Delta: how far each component moves the station¶
The Birnbaum importance is ∂A/∂A_i, the station's change per unit of a
component's availability:
station.birnbaum_importance() # {'pump1': 0.0273, 'pump2': 0.0273, 'valve': 0.9992}
station.birnbaum_importance(x=[5.0, 40.0])["pump1"] # array([0.005 , 0.0279])
The valve is in series: the station works whenever it does, unless both pumps are down. A pump matters only while the other is down, 2.7% of the time in the long run, and a fifth as often at five days, while both are new.
Parameter deltas: which lever¶
parameter_sensitivity() differentiates the long-run availability in each
lever: here the life and repair models' parameters and the maintenance's
interval and duration (and, where there are any, one more standby unit or
repair crew):
levers = station.parameter_sensitivity()
levers["valve"]["reliability.beta"] # -> 0.001286
levers["valve"]["repairability.failure_rate"] # -> 0.001055
levers["pump1"]["repairability.failure_rate"] # -> 0.000729
levers["valve"]["preventive.duration.failure_rate"] # -> 0.000622
levers["valve"]["preventive.interval"] # -> 0.00003
Each is per unit of its lever, and the units differ: the valve's Weibull
shape has none, a repair rate is per day, the interval is in days. Given
what a unit of each costs, unit_costs= ranks them by availability per
unit spent; the shares below put them on one footing instead.
The levers themselves¶
levers() lists the levers that parameter_sensitivity moves, in its
order, each a Lever: whose it is and its name, its value now, the range
of values it can take, whether it is discrete (one more standby unit or
crew), and whether it moves a calendar that its component shares with
others. Nothing is worked out, so a report can name the levers and show
their values without the sensitivities:
valve = {lever.name: lever for lever in station.levers() if lever.key == "valve"}
len(valve) # -> 5
valve["repairability.failure_rate"].value # -> 2.0
valve["preventive.interval"].calendar # False: no other unit shares its calendar
with_levers builds the station with levers moved, as
parameter_sensitivity moves them, so that a what-if agrees with the
deltas. With the valve repaired 10% faster, in 0.4545 days rather than
half a day:
faster = station.with_levers({valve["repairability.failure_rate"]: 2.2})
faster.repairability["valve"].mean() # -> 0.4545
faster.mean_availability() # -> 0.99482
The station gains 0.00019, a little less than the delta times the step,
0.001055 × 0.2 = 0.00021: the gain from faster repairs tapers off.
DIM: shares of a change¶
The differential importance is each component's (or lever's) share of the change when they all change together, so the shares add up, and a group's is the sum of its members'. Among the components, moved by as much each:
station.differential_importance() # {'pump1': 0.0259, 'pump2': 0.0259, 'valve': 0.9482}
Over the levers, each moved by the same fraction of itself, the way that
improves the station (improving=True), and grouped by kind:
kinds = {
"lives": [(n, f"reliability.{p}")
for n in ("pump1", "pump2", "valve") for p in ("alpha", "beta")],
"repairs": [(n, "repairability.failure_rate")
for n in ("pump1", "pump2", "valve")],
"maintenance": [("valve", "preventive.interval"),
("valve", "preventive.duration.failure_rate")],
}
gain = station.differential_importance(
over="parameters", change="proportional", improving=True, groups=kinds
)
gain["lives"] # -> 0.4003
gain["repairs"] # -> 0.2536
gain["maintenance"] # -> 0.346
Among the components the valve holds 95% of the change, but the levers spread the gain: 40% of it lies in the lives, a quarter in the repairs, and a third in the valve's maintenance, its two levers alone.
Gamma: complements or substitutes¶
The joint importance ∂²A/∂A_i ∂A_j says whether improving two together
is worth more than the sum of improving each:
joint = station.joint_importance()
joint[("pump1", "pump2")] # -> -0.9954
joint[("pump1", "valve")] # -> 0.0274
The pumps are substitutes (negative): either does the other's job, so improving both buys less than the sum of improving each. A pump and the valve are complements (positive), but weakly: a better valve makes a better pump worth a little more.
Theta: what is moving the station now¶
The station's availability changes as its components age, fail and come
back. availability_rate(x) gives its rate of change, from new, split
among the components: dA/dt = Σ I_B^i dA_i/dt, each term what that
component is doing to the station then:
rate = station.availability_rate([5.0, 81.0])
rate.rate[0] # -> -9.9e-05
rate.node_rate["valve"][0] # -> -8.7e-05
rate.node_rate["pump1"][0] # -> -6e-06
At five days the availability is falling by about 1e-4 a day, nine-tenths of it the valve's, wearing in series. At 80 days every valve still in its first life is replaced, together, and the availability jumps; the jumps are reported apart, split among the components that make them:
rate.jump_times[0] # -> 80.0
rate.node_jumps["valve"][0] # -> -0.7017
rate.node_rate["valve"][1] # -> 0.0518
Just after 80 days the station is down for the replacement with probability 0.70, the chance that its valve reached 80 days without failing: of a fleet of stations commissioned together, seven in ten would be down for it at once, a reason to stagger their first replacements. A day later, the replacements still under way are ending at 5% a day.
Theta, integrated: who caused the failures¶
Integrated, the parts give the Barlow–Proschan importance: each component's share of the station's failures, the ones in which its failure found the station up and left it down:
station.barlow_proschan_importance() # {'pump1': 0.1305, 'pump2': 0.1305, 'valve': 0.7389}
station.barlow_proschan_importance(window=100.0)["valve"] # -> 0.7567
The valve causes three of the station's failures in four, in the long run and over its first 100 days alike; a pump causes one only when the other is down already. A planned outage is no failure: the replacements at 80 days count in neither.
Vega: whose uncertainty widens the answer¶
The models above are known exactly. Fitted to failure data, they are estimates, and the answer is uncertain; vega says whose uncertainty makes it so, and so where more data would narrow it most. Give the station's pumps a life fitted to 12 failures, and its valve one fitted to 30:
import numpy as np
pump_fit = surv.Weibull.fit(W([40, 2.0]).qf(np.linspace(0.04, 0.96, 12)))
valve_fit = surv.Weibull.fit(W([160, 1.5]).qf(np.linspace(0.02, 0.98, 30)))
fitted = RepairableRBD(
[("s", "pump1"), ("s", "pump2"),
("pump1", "valve"), ("pump2", "valve"),
("valve", "t")],
{
"pump1": unit(pump_fit, E([1.0])),
"pump2": unit(pump_fit, E([1.0])),
"valve": unit(
valve_fit,
E([2.0]),
preventive={"policy": "age", "interval": 80.0, "duration": E([4.0])},
),
},
)
spread = fitted.mean_availability_uncertainty(n_draws=400, seed=0)
spread.interval(0.9) # (0.9937, 0.9954)
vega = fitted.uncertainty_importance()
vega.first_order["valve"] # -> 0.81
vega.first_order[("pump1", "pump2")] # -> 0.19
Each draw gives the fits plausible parameters, from their covariance, and works the station's availability out as the station's own is worked out. The two pumps share one fit, so they are one input, keyed by the pair. Four-fifths of the variance of the long-run availability is the valve's fit: the valve is in series, and its life sets how often the station stops, where a pump's failure stops it only while the other is down. More valve failures, not pump failures, would narrow the interval.
The question decides whose data to collect. Unattended, never repaired, the
station is a NonRepairableRBD on the same diagram, and its reliability
from new is the pumps' to lose:
from repyability import NonRepairableRBD
unattended = NonRepairableRBD(
[("s", "pump1"), ("s", "pump2"),
("pump1", "valve"), ("pump2", "valve"),
("valve", "t")],
{"pump1": pump_fit, "pump2": pump_fit, "valve": valve_fit},
)
unattended.uncertainty_importance(20.0).first_order[("pump1", "pump2")] # -> 0.79
unattended.uncertainty_importance(of="mean").first_order[("pump1", "pump2")] # -> 0.95
Unrepaired, a pump's failure is for good, and the pair's wear sets the
station's life: most of the variance of the reliability at 20 days, and
nearly all of the MTTF's, is the pumps' fit. By default each input's share
is the delta method's, which adds up to 1; method="sobol" estimates Sobol
indices from draws instead, which take the quantity's nonlinearity in, and
sampling="sobol" takes the draws from a scrambled Sobol sequence, which
settles with fewer of them than random draws do.
Rho: to come¶
Rho, the sensitivity of a plan's value to the discount rate, waits for the
plan's net present value. total_cost already discounts the costs of
ownership (discount_rate=, see Costs).
Reading them together¶
- Delta and DIM say the valve is where the station's availability is won or lost: 95% of an equal improvement in every component's availability.
- The levers say how: of the gain from improving every lever by the same fraction, a third lies in the valve's maintenance alone, more than in all the repairs together.
- Gamma says the pumps are substitutes, so improving both pays less than the sum, and a pump and the valve need no common campaign.
- Theta says what moves the availability from new: the valve's wear, and its first replacement, due at 80 days for every valve that has not failed by then.
- Barlow–Proschan says the valve causes three failures in four.
- Vega says the valve's failure data, not the pumps', is what to collect more of, for the station as it is run; left unattended, the pumps' would be.
Which class has which¶
| Measure | NonRepairableRBD |
RepairableRBD |
FaultTree |
|---|---|---|---|
birnbaum_importance |
at x |
long run, x, window, state |
at t |
parameter_sensitivity |
at x |
long run, x, window, state; of="cost_rate" |
|
levers, with_levers |
the parameters | every lever | |
differential_importance |
at x |
long run, x, window, state |
at t |
joint_importance |
at x |
long run, x, window, state |
at t |
| Theta | reliability_rate(x) |
availability_rate(x), from new or state |
|
barlow_proschan_importance |
whole life, or by x |
long run, window, state |
|
uncertainty_importance |
at x; of= the MTTF, a B-life, a time to a reliability |
long run; of= the availability at x or over missions x, from new or state, or the cost rate |
With limited repair crews or common-cause groups, the measures follow the
crews' and the groups' Markov chains: theta and the Barlow–Proschan shares
split the chains' transitions by the component, or common cause, that makes
each (#199). With common-cause groups joint_importance is refused, as
their members cannot be held.