Fault trees, phased missions and networks¶
FaultTree ¶
FaultTree(gates, events, top=None, *, ccf_groups=None)
A static fault tree: gates over basic events.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
gates
|
dict
|
|
required |
events
|
dict
|
|
required |
top
|
Hashable
|
The top event, a gate. By default the one gate that is no other gate's input. |
None
|
ccf_groups
|
list of CCFGroup
|
Common-cause groups over basic events (keyword only), as a
|
None
|
Attributes:
| Name | Type | Description |
|---|---|---|
top |
Hashable
|
The top event. |
gates |
dict
|
|
events |
dict
|
|
repeated_events |
frozenset
|
The events that feed more than one gate. |
is_fixed |
bool
|
Whether no event's probability depends on time, so that |
ccf_groups |
list of CCFGroup
|
The common-cause groups, as given. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If a gate is malformed, an input is neither a gate nor an event, a name is both, the gates form a loop, a gate or event is not below the top event, the top event is not a gate (or cannot be inferred), an event's model is invalid, or a common-cause group's member is not an event, is in two groups, or has a model the others do not. |
Examples:
Cooling is lost if both pumps fail or the valve fails:
>>> from repyability import FaultTree
>>> tree = FaultTree(
... {
... "no cooling": ("or", ["no flow", "valve"]),
... "no flow": ("and", ["pump 1", "pump 2"]),
... },
... {"pump 1": 0.1, "pump 2": 0.1, "valve": 0.05},
... )
>>> tree.top
'no cooling'
>>> round(tree.top_event_probability(), 4) # 1 - (1 - 0.01) * 0.95
0.0595
>>> [sorted(c) for c in tree.minimal_cut_sets()]
[['valve'], ['pump 1', 'pump 2']]
top_event_probability ¶
top_event_probability(t=None)
The probability that the top event has occurred.
Exact, repeated events included. With lifetime models it is the
probability that the top event has occurred by time t: the
unreliability of the system the tree describes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
t
|
array_like
|
Time/s, a number or an array. May be left out when no event's probability depends on time. |
None
|
Returns:
| Type | Description |
|---|---|
float or ndarray
|
The probability, a float for a number |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Examples:
A power supply shared by two redundant channels (a repeated event). Treating its two appearances as independent would give 0.0119, too low: one supply failure takes out both channels.
>>> from repyability import FaultTree
>>> tree = FaultTree(
... {
... "no output": ("and", ["channel A", "channel B"]),
... "channel A": ("or", ["supply", "pump A"]),
... "channel B": ("or", ["supply", "pump B"]),
... },
... {"supply": 0.01, "pump A": 0.1, "pump B": 0.1},
... )
>>> round(tree.top_event_probability(), 4) # 0.01 + 0.99 * 0.1 ** 2
0.0199
ff ¶
ff(t=None)
The probability that the top event has occurred by t: the
unreliability of the system the tree describes, by the name an
RBD's ff has (the same as top_event_probability).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
t
|
array_like
|
Time/s, as for |
None
|
Returns:
| Type | Description |
|---|---|
float or ndarray
|
The probability. |
sf ¶
sf(t=None)
The probability that the top event has not occurred by t: the
reliability of the system the tree describes, as an RBD's sf
gives it. Worked out in its own right, not as one less ff, so a
small one keeps its precision.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
t
|
array_like
|
Time/s, as for |
None
|
Returns:
| Type | Description |
|---|---|
float or ndarray
|
The probability. |
Examples:
>>> from repyability import FaultTree
>>> tree = FaultTree(
... {"top": ("and", ["a", "b"])}, {"a": 0.1, "b": 0.2}
... )
>>> round(tree.sf(), 4), round(tree.ff(), 4)
(0.98, 0.02)
Hf ¶
Hf(t=None)
The cumulative hazard H(t) = -ln R(t) of the system the tree
describes, R its sf: exact (so
it honours the common-cause groups), +inf where the top event
is certain. While the top event is less likely than not it is
-log1p(-F(t)) from its probability F, so that a small one
keeps its precision, as NonRepairableRBD.Hf has it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
t
|
array_like
|
Time/s, a number or an array. May be left out when no event's probability depends on time. |
None
|
Returns:
| Type | Description |
|---|---|
float or ndarray
|
The cumulative hazard: a float for a number |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Examples:
One exponential event's cumulative hazard is its rate times t:
>>> import surpyval as surv
>>> from repyability import FaultTree
>>> tree = FaultTree(
... {"top": ("or", ["c"])},
... {"c": surv.Exponential.from_params([0.01])},
... )
>>> round(tree.Hf(50), 6)
0.5
df ¶
df(t=None, dx=1e-06)
The density of the time to the top event, f(t) = -dR/dt.
By a central finite difference of the exact
sf (so it honours the common-cause
groups), with step dx * |t|, relative to t (at 0, dx
of the shortest of the events' typical failure times), as
NonRepairableRBD.df takes the diagram's: the difference is of
the top event probability while it is the smaller, which keeps
its precision, the lower point is clipped at 0, and a negative
result (rounding) is clipped to 0. It assumes the probability is
smooth near t, which does not hold where an event's model has a
kink or a step; a tree of probabilities alone has a density of 0.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
t
|
array_like
|
Time/s, a number or an array. May be left out when no event's probability depends on time. |
None
|
dx
|
float
|
The relative step of the difference, by default 1e-6. |
1e-06
|
Returns:
| Type | Description |
|---|---|
float or ndarray
|
The density: a float for a number |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Examples:
One exponential event: f(t) = rate * exp(-rate * t).
>>> import surpyval as surv
>>> from repyability import FaultTree
>>> tree = FaultTree(
... {"top": ("or", ["c"])},
... {"c": surv.Exponential.from_params([0.01])},
... )
>>> round(tree.df(50), 6)
0.006065
hf ¶
hf(t=None, dx=1e-06)
The hazard rate of the top event, h(t) = f(t) / R(t): the
numerical density of df over the
exact sf (both honour the
common-cause groups), +inf where the top event is certain.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
t
|
array_like
|
Time/s, a number or an array. May be left out when no event's probability depends on time. |
None
|
dx
|
float
|
The relative step of the difference, passed to |
1e-06
|
Returns:
| Type | Description |
|---|---|
float or ndarray
|
The hazard rate: a float for a number |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Examples:
One exponential event has a constant hazard rate:
>>> import surpyval as surv
>>> from repyability import FaultTree
>>> tree = FaultTree(
... {"top": ("or", ["c"])},
... {"c": surv.Exponential.from_params([0.01])},
... )
>>> round(tree.hf(50), 6)
0.01
mean ¶
mean(method='exact', *, mc_samples=None, seed=None)
The mean time to the top event: the MTTF of the system the tree describes.
Exact to about 1e-10, relative: the area under the probability
that the top event has not occurred, integral from 0 to infinity
of R(t) dt with R the exact sf,
integrated by adaptive quadrature on pieces that start at the
events' models' kinks, as NonRepairableRBD.mean integrates the
diagram's (it is as exact as the events' models' own
probabilities: see analysis_routes). inf if the top event
may never occur (an event of a limited failure population). An
event given as a probability has occurred, or not, from the start
and for good, as sf takes it. A common-cause group that splits
the failure rate (basis="rate") is included; one splitting a
probability is refused, as the diagram refuses it.
method="simulate" estimates it from mc_samples lifetimes
of the tree's diagram (random):
the draws are the diagram's, to_rbd().mean(method="simulate").
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
method
|
str
|
|
'exact'
|
mc_samples
|
int
|
With |
None
|
seed
|
int
|
With |
None
|
Returns:
| Type | Description |
|---|---|
float
|
The mean time to the top event ( |
Raises:
| Type | Description |
|---|---|
ValueError
|
If every event is a probability, so that the top event has no
time; if |
NotImplementedError
|
With a common-cause group that splits a failure probability
(the default basis): its model assumes it is small (see
|
Examples:
Either of two exponential events, with rates 0.01 and 0.03, is a failure rate of 0.04: a mean time of 25.
>>> import surpyval as surv
>>> from repyability import FaultTree
>>> E = surv.Exponential.from_params
>>> tree = FaultTree(
... {"top": ("or", ["a", "b"])}, {"a": E([0.01]), "b": E([0.03])}
... )
>>> round(tree.mean(), 8)
25.0
>>> round(tree.mean(method="simulate", mc_samples=10_000, seed=1), 1)
25.3
random ¶
random(size, seed=None)
size random times to the top event (Monte-Carlo): the
lifetimes of the tree's diagram,
NonRepairableRBD.random
of to_rbd() (converted once), so the draws are the diagram's
(an event given as a probability occurs at the start, with its
probability, or never; a common-cause group that splits a
probability is left out, as there).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
size
|
int
|
The number of lifetimes. |
required |
seed
|
int
|
Seeds the draws, by default None. |
None
|
Returns:
| Type | Description |
|---|---|
ndarray
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Examples:
>>> import surpyval as surv
>>> from repyability import FaultTree
>>> E = surv.Exponential.from_params
>>> tree = FaultTree(
... {"top": ("or", ["a", "b"])}, {"a": E([0.01]), "b": E([0.03])}
... )
>>> times = tree.random(5, seed=1)
>>> times.shape, bool((times == tree.to_rbd().random(5, seed=1)).all())
((5,), True)
time_to_reliability ¶
time_to_reliability(target, upper_bound=None)
The time at which the probability that the top event has not
occurred falls to target: the inverse of
sf (so it honours the common-cause
groups), found by Brent's method between 0 and an upper bound
found by doubling from 1 unless given, as
NonRepairableRBD.time_to_reliability finds the diagram's.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
target
|
float or array_like
|
The reliability to solve for, in (0, 1), or several. |
required |
upper_bound
|
float
|
A time at which the reliability is below |
None
|
Returns:
| Type | Description |
|---|---|
float or ndarray
|
The time at which |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Examples:
>>> import surpyval as surv
>>> from repyability import FaultTree
>>> tree = FaultTree(
... {"top": ("or", ["c"])},
... {"c": surv.Weibull.from_params([100, 2])},
... )
>>> round(tree.time_to_reliability(0.9), 2)
32.46
bx_life ¶
bx_life(x, upper_bound=None)
The Bx life: the time by which the top event has occurred in
x percent of systems, time_to_reliability(1 - x / 100).
bx_life(10) is the B10 life.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
float or array_like
|
The percentage in which the top event has occurred (10 for the B10 life, not a fraction), in (0, 100), or several. |
required |
upper_bound
|
float
|
As for |
None
|
Returns:
| Type | Description |
|---|---|
float or ndarray
|
The time (one for each |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Examples:
>>> import surpyval as surv
>>> from repyability import FaultTree
>>> tree = FaultTree(
... {"top": ("or", ["c"])},
... {"c": surv.Weibull.from_params([100, 2])},
... )
>>> round(tree.bx_life(10), 2)
32.46
occurs ¶
occurs(events)
Whether the top event occurs when exactly events have.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
events
|
Collection[Hashable]
|
The basic events that have occurred; every other has not. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
Whether the top event occurs. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If an event is unknown. |
Examples:
>>> from repyability import FaultTree
>>> tree = FaultTree(
... {"top": ("vote", 2, ["a", "b", "c"])},
... {"a": 0.1, "b": 0.1, "c": 0.1},
... )
>>> tree.occurs({"a"}), tree.occurs({"a", "c"})
(False, True)
minimal_cut_sets ¶
minimal_cut_sets()
The minimal cut sets: the smallest sets of basic events whose joint occurrence makes the top event occur.
Returns:
| Type | Description |
|---|---|
list of frozenset
|
The minimal cut sets, smallest first (then by name). |
Examples:
>>> from repyability import FaultTree
>>> tree = FaultTree(
... {"top": ("vote", 2, ["a", "b", "c"])},
... {"a": 0.1, "b": 0.1, "c": 0.1},
... )
>>> [sorted(c) for c in tree.minimal_cut_sets()]
[['a', 'b'], ['a', 'c'], ['b', 'c']]
get_min_cut_sets ¶
get_min_cut_sets()
The minimal cut sets as a set, as an RBD's get_min_cut_sets
gives them (minimal_cut_sets lists them).
Returns:
| Type | Description |
|---|---|
set of frozenset
|
The minimal cut sets. |
get_min_path_sets ¶
get_min_path_sets()
The minimal path sets as a set, as an RBD's
get_min_path_sets(include_in_out_nodes=False) gives them
(minimal_path_sets lists them).
Returns:
| Type | Description |
|---|---|
set of frozenset
|
The minimal path sets. |
minimal_path_sets ¶
minimal_path_sets()
The minimal path sets: the smallest sets of basic events whose not occurring keeps the top event from occurring.
Returns:
| Type | Description |
|---|---|
list of frozenset
|
The minimal path sets, smallest first (then by name). |
Examples:
>>> from repyability import FaultTree
>>> tree = FaultTree(
... {"top": ("or", ["a", "both"]), "both": ("and", ["b", "c"])},
... {"a": 0.1, "b": 0.1, "c": 0.1},
... )
>>> [sorted(p) for p in tree.minimal_path_sets()]
[['a', 'b'], ['a', 'c']]
ranked_cut_sets ¶
ranked_cut_sets(t=None)
The minimal cut sets with their probabilities, most likely first.
A cut set's probability is that all its events occur: the product of theirs, or with common-cause groups, as the groups' shared causes add to it (a cut set of two members of a group is often the largest). The largest say which combinations of failures dominate the top event.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
t
|
float
|
The time, a single number. May be left out when no event's probability depends on time. |
None
|
Returns:
| Type | Description |
|---|---|
list of tuple
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Examples:
>>> from repyability import FaultTree
>>> tree = FaultTree(
... {"top": ("or", ["valve", "flow"]),
... "flow": ("and", ["pump 1", "pump 2"])},
... {"pump 1": 0.1, "pump 2": 0.1, "valve": 0.005},
... )
>>> [(sorted(c), round(p, 4)) for c, p in tree.ranked_cut_sets()]
[(['pump 1', 'pump 2'], 0.01), (['valve'], 0.005)]
birnbaum_importance ¶
birnbaum_importance(t=None)
Birnbaum importance of each basic event.
In the guide's Greeks it is delta: how far the system moves with each basic event (see Sensitivities: the Greeks).
P(top | e occurred) - P(top | e did not): how much the top event
probability depends on the event, the rate at which it rises with
the event's probability.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
t
|
array_like
|
Time/s, a number or an array. May be left out when no event's probability depends on time. |
None
|
Returns:
| Type | Description |
|---|---|
dict
|
|
Examples:
>>> from repyability import FaultTree
>>> tree = FaultTree(
... {"top": ("or", ["valve", "flow"]),
... "flow": ("and", ["pump 1", "pump 2"])},
... {"pump 1": 0.1, "pump 2": 0.1, "valve": 0.05},
... )
>>> {e: round(v, 4) for e, v in tree.birnbaum_importance().items()}
{'pump 1': 0.095, 'pump 2': 0.095, 'valve': 0.99}
criticality_importance ¶
criticality_importance(t=None, kind='failure')
Criticality importance of each basic event.
kind="failure" (the default): I_B(e) * P(e) / P(top), the
probability that the event has occurred and is critical, given
that the top event has occurred, so the share of the top event it
accounts for; nan where the top event cannot occur.
kind="success": I_B(e) * (1 - P(e)) / (1 - P(top)), the
probability that the event has not occurred and is critical, given
that the top event has not, as
NonRepairableRBD.criticality_importance
defines both forms; nan where the top event is certain.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
t
|
array_like
|
Time/s, a number or an array. May be left out when no event's probability depends on time. |
None
|
kind
|
str
|
|
'failure'
|
Returns:
| Type | Description |
|---|---|
dict
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Examples:
>>> from repyability import FaultTree
>>> tree = FaultTree(
... {"top": ("or", ["valve", "flow"]),
... "flow": ("and", ["pump 1", "pump 2"])},
... {"pump 1": 0.1, "pump 2": 0.1, "valve": 0.05},
... )
>>> {e: round(v, 4) for e, v in tree.criticality_importance().items()}
{'pump 1': 0.1597, 'pump 2': 0.1597, 'valve': 0.8319}
risk_achievement_worth ¶
risk_achievement_worth(t=None)
Risk achievement worth of each basic event.
P(top | e occurred) / P(top): how many times more likely the top
event is once the event has occurred.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
t
|
array_like
|
Time/s, a number or an array. May be left out when no event's probability depends on time. |
None
|
Returns:
| Type | Description |
|---|---|
dict
|
|
Examples:
>>> from repyability import FaultTree
>>> tree = FaultTree(
... {"top": ("or", ["valve", "flow"]),
... "flow": ("and", ["pump 1", "pump 2"])},
... {"pump 1": 0.1, "pump 2": 0.1, "valve": 0.05},
... )
>>> {e: round(v, 2) for e, v in tree.risk_achievement_worth().items()}
{'pump 1': 2.44, 'pump 2': 2.44, 'valve': 16.81}
risk_reduction_worth ¶
risk_reduction_worth(t=None)
Risk reduction worth of each basic event.
P(top) / P(top | e did not occur): the factor by which the top
event would be less likely if the event could not occur.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
t
|
array_like
|
Time/s, a number or an array. May be left out when no event's probability depends on time. |
None
|
Returns:
| Type | Description |
|---|---|
dict
|
|
Examples:
>>> from repyability import FaultTree
>>> tree = FaultTree(
... {"top": ("or", ["valve", "flow"]),
... "flow": ("and", ["pump 1", "pump 2"])},
... {"pump 1": 0.1, "pump 2": 0.1, "valve": 0.05},
... )
>>> {e: round(v, 3) for e, v in tree.risk_reduction_worth().items()}
{'pump 1': 1.19, 'pump 2': 1.19, 'valve': 5.95}
fussell_vesely ¶
fussell_vesely(t=None, fv_type='c', method='exact')
Fussell-Vesely importance of each basic event.
The share of the top event probability carried by the minimal cut
sets that contain the event: the probability that one of them has
occurred (every event in it), over the top event probability, as
NonRepairableRBD.fussell_vesely computes it. It is exact, and
between 0 and 1, by default; method="rare_event" sums the cut
sets' probabilities instead (the rare-event approximation of their
union, as many PRA tools report it), which can exceed 1 when the
events are not rare. nan where the top event cannot occur.
With fv_type="p" the minimal path sets (see
minimal_path_sets)
take the place of the cut sets, as the diagram's fv_type="p"
does: the probability that every event of some path set
containing the event has occurred.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
t
|
array_like
|
Time/s, a number or an array. May be left out when no event's probability depends on time. |
None
|
fv_type
|
str
|
|
'c'
|
method
|
str
|
|
'exact'
|
Returns:
| Type | Description |
|---|---|
dict
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Examples:
>>> from repyability import FaultTree
>>> tree = FaultTree(
... {"top": ("or", ["valve", "flow"]),
... "flow": ("and", ["pump 1", "pump 2"])},
... {"pump 1": 0.1, "pump 2": 0.1, "valve": 0.05},
... )
>>> {e: round(v, 4) for e, v in tree.fussell_vesely().items()}
{'pump 1': 0.1681, 'pump 2': 0.1681, 'valve': 0.8403}
differential_importance ¶
differential_importance(t=None, *, change='uniform', kind='failure', groups=None)
Each basic event's share of the change in the top event probability when they all change together: the differential importance measure (DIM, Borgonovo & Apostolakis, 2001).
In the guide's Greeks it is DIM, the shares of a change (see Sensitivities: the Greeks).
DIM_e = dP/dq_e dq_e / sum_f dP/dq_f dq_f, with q_e the
events' probabilities, so the shares add up to 1, and a group's
share is the sum of its members' (groups): what share of a
possible gain lies in the pumps, say. change="uniform" moves
every probability by as much, which shares out the Birnbaum
importance; "proportional" each by the same fraction of
itself, which shares out the criticality importance. With
common-cause groups, a member's measures are conditioned through
the groups' outcomes, as birnbaum_importance and
criticality_importance work them out. NaN where the shares'
total is 0 (the top event cannot occur, or cannot be helped).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
t
|
array_like
|
Time/s, a number or an array. May be left out when no event's probability depends on time. |
None
|
change
|
str
|
|
'uniform'
|
kind
|
str
|
With |
'failure'
|
groups
|
dict
|
|
None
|
Returns:
| Type | Description |
|---|---|
dict
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Examples:
>>> from repyability import FaultTree
>>> tree = FaultTree(
... {"top": ("or", ["valve", "flow"]),
... "flow": ("and", ["pump 1", "pump 2"])},
... {"pump 1": 0.1, "pump 2": 0.1, "valve": 0.05},
... )
>>> shares = tree.differential_importance(
... groups={"pumps": ["pump 1", "pump 2"], "valve": ["valve"]}
... )
>>> {g: round(v, 4) for g, v in shares.items()}
{'pumps': 0.161, 'valve': 0.839}
joint_importance ¶
joint_importance(t=None)
The joint (second-order) importance of each pair of basic events: whether preventing the two together is worth more than preventing each.
In the guide's Greeks it is gamma: complements or substitutes (see Sensitivities: the Greeks).
JRI(e, f) = -d2P / dq_e dq_f, P the top event probability
and q the events' probabilities: how much event f's
Birnbaum importance falls when event e goes from occurring to
not. It is the joint importance of the diagram the tree describes
(see to_rbd), in its reliability. Positive, the two are
complements, as under an OR gate: preventing either makes
preventing the other worth more. Negative, they are substitutes, as
under an AND gate: either one prevented keeps the gate from
occurring. Exact, from the top event probability with both events
held, as the tree is multilinear in the events. With common-cause
groups, the top event probability given each of the pair occurring
and not is conditioned on their states, as birnbaum_importance
conditions on one (two members of one group on their joint state),
and an event outside the groups is held.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
t
|
array_like
|
Time/s, a number or an array. May be left out when no event's probability depends on time. |
None
|
Returns:
| Type | Description |
|---|---|
dict
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Examples:
>>> from repyability import FaultTree
>>> tree = FaultTree(
... {"top": ("or", ["valve", "flow"]),
... "flow": ("and", ["pump 1", "pump 2"])},
... {"pump 1": 0.1, "pump 2": 0.1, "valve": 0.05},
... )
>>> joint = tree.joint_importance()
>>> round(joint[("pump 1", "pump 2")], 4) # substitutes
-0.95
>>> round(joint[("pump 1", "valve")], 4) # complements
0.1
structural_importance ¶
structural_importance()
The structural importance of each basic event: the fraction of the states of the other events in which it is pivotal, the top event occurring when it has and not when it has not.
It is the Birnbaum importance with every event's probability at
1/2, so it depends on the gates alone, not on the events' models
or the common-cause groups: useful at design time, before any
data. It is the diagram's structural_importance (see
to_rbd).
Returns:
| Type | Description |
|---|---|
dict
|
|
Examples:
The valve is pivotal unless both pumps have failed (three states of four), and a pump when the other has failed and the valve has not (one of four):
>>> from repyability import FaultTree
>>> tree = FaultTree(
... {"top": ("or", ["valve", "flow"]),
... "flow": ("and", ["pump 1", "pump 2"])},
... {"pump 1": 0.1, "pump 2": 0.1, "valve": 0.05},
... )
>>> tree.structural_importance()
{'pump 1': 0.25, 'pump 2': 0.25, 'valve': 0.75}
improvement_potential ¶
improvement_potential(t=None)
The improvement potential of each basic event: P(top) - P(top
| e did not occur), how far the top event probability would fall
if the event could not occur. It is I_B(e) * q_e, the Birnbaum
importance times the event's probability, and is worked out as
that product, so a small one keeps its precision; with common-cause
groups, a member's is conditioned through the groups' outcomes, as
birnbaum_importance is.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
t
|
array_like
|
Time/s, a number or an array. May be left out when no event's probability depends on time. |
None
|
Returns:
| Type | Description |
|---|---|
dict
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Examples:
A valve that could not fail would take the top event from 0.0595 to the pumps' 0.01:
>>> from repyability import FaultTree
>>> tree = FaultTree(
... {"top": ("or", ["valve", "flow"]),
... "flow": ("and", ["pump 1", "pump 2"])},
... {"pump 1": 0.1, "pump 2": 0.1, "valve": 0.05},
... )
>>> {e: round(v, 4) for e, v in tree.improvement_potential().items()}
{'pump 1': 0.0095, 'pump 2': 0.0095, 'valve': 0.0495}
barlow_proschan_importance ¶
barlow_proschan_importance(t=None)
Each basic event's Barlow-Proschan importance: the probability
that the top event is caused by the event's occurring (Barlow &
Proschan, 1975), given that it has occurred by t, or ever.
In the guide's Greeks it is theta, integrated: who caused the failures (see Sensitivities: the Greeks).
An event's occurring causes the top event when the event is
critical then, so the probability that the top event has occurred
by t through event e is integral_0^t I_B^e(s) f_e(s)
ds, its Birnbaum importance times its model's density, and its
share of the sum over the events is its importance: the shares add
up to 1. A common-cause group's part is its members' together,
under the tuple of their names (the top event probability's change
with the members' lives moved on, by differences). The integrals
are by adaptive quadrature to about 1e-9, as
NonRepairableRBD.barlow_proschan_importance works out the
diagram's. An event given as a probability has no density, so no
share; with every event one, the top event occurs at the start or
never, and the measure is refused.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
t
|
array_like
|
Time/s by which the top event has occurred, a number or an array; by default over the whole life. |
None
|
Returns:
| Type | Description |
|---|---|
dict
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
For a negative time, or if every event is a probability (the system then has no lifetimes). |
Examples:
Either of two exponential events, with rates 1 and 3: the second causes three quarters of the top events.
>>> import surpyval as surv
>>> from repyability import FaultTree
>>> E = surv.Exponential.from_params
>>> tree = FaultTree(
... {"top": ("or", ["a", "b"])}, {"a": E([1.0]), "b": E([3.0])}
... )
>>> shares = tree.barlow_proschan_importance()
>>> {e: round(v, 6) for e, v in shares.items()}
{'a': 0.25, 'b': 0.75}
parameter_sensitivity ¶
parameter_sensitivity(t=None, rel_step=1e-05)
The sensitivity of the probability that the top event has not
occurred (sf) to each parameter of the events' models.
In the guide's Greeks it is the levers' deltas (see Sensitivities: the Greeks).
For event e with parameter theta, d sf / d theta =
I_B(e) * d sf_e(t; theta) / d theta: its Birnbaum importance
times how far its model's reliability moves with the parameter,
the latter by a central difference with step rel_step *
|theta| (rel_step if theta is 0), the model rebuilt with
from_params (one-sided where one side is not a valid value, nan
where neither is), as NonRepairableRBD.parameter_sensitivity
works out the diagram's. An event given as a probability has one
parameter, p, the probability (as a FixedEventProbability
names it). The members of a common-cause group carry one model,
so its parameters are reported once, under the tuple of the
members, each moved for every member at once, and so are the
group's own ("ccf_beta", and an MGL
model's "ccf_gamma", ...), each by a central difference of
the exact probabilities with the groups. levers() lists the
parameters, with their values and ranges, and with_levers
builds the tree with them moved.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
t
|
array_like
|
Time/s, a number or an array. May be left out when no event's probability depends on time. |
None
|
rel_step
|
float
|
The relative step of the differences, by default 1e-5. |
1e-05
|
Returns:
| Type | Description |
|---|---|
dict
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Examples:
One exponential event: R = exp(-rate t), so dR/d rate = -t
exp(-rate t), about -9.048 at t = 10 with rate 0.01:
>>> import surpyval as surv
>>> from repyability import FaultTree
>>> tree = FaultTree(
... {"top": ("or", ["c"])},
... {"c": surv.Exponential.from_params([0.01])},
... )
>>> sens = tree.parameter_sensitivity(10)
>>> {name: round(v, 3) for name, v in sens["c"].items()}
{'failure_rate': -9.048}
levers ¶
levers()
The parameters parameter_sensitivity moves, in the order it
reports them: each event's model's, by surpyval's names (p
for an event given as a probability), and each common-cause
group's under the tuple of its members (the one model its members
carry, then its own, "ccf_beta", ...), each a
Lever with its value and range, as a
diagram's levers() lists them. Nothing is worked out;
with_levers sets them. The
levers are the events' models' own parameters: those of the units
inside a composite model (a StandbyModel, a
LoadSharingModel, a DegradingNode) or of a nested diagram
are not reached (see the guide's Greeks).
Returns:
| Type | Description |
|---|---|
list of Lever
|
The levers, in |
Examples:
>>> import surpyval as surv
>>> from repyability import FaultTree
>>> tree = FaultTree(
... {"top": ("or", ["c", "d"])},
... {"c": surv.Weibull.from_params([100, 2]), "d": 0.05},
... )
>>> for lever in tree.levers():
... print(lever.key, lever.name, lever.value, lever.bounds)
c alpha 100.0 (0.0, inf)
c beta 2.0 (0.0, inf)
d p 0.05 (0.0, 1.0)
with_levers ¶
with_levers(values)
A copy of this tree with parameters at new values, each moved
as parameter_sensitivity moves it: an event's model rebuilt
with the parameter changed (an event given as a probability keeps
being one), a common-cause group's members' one model for every
member, a group's own ("ccf_beta", ...) in its model.
levers() lists the parameters.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
values
|
dict
|
Each parameter's new value, by its
|
required |
Returns:
| Type | Description |
|---|---|
FaultTree
|
The new tree, built as this one was with the parameters moved; this one is unchanged. |
Raises:
| Type | Description |
|---|---|
ValueError
|
For a parameter the tree does not have (with the closest
name), or a value its model refuses (one outside the lever's
|
TypeError
|
If |
Examples:
Twice the Weibull scale: at 100 the top event has not occurred
with probability exp(-(100 / 200) ** 2), about 0.7788, where
it had exp(-1):
>>> import surpyval as surv
>>> from repyability import FaultTree
>>> tree = FaultTree(
... {"top": ("or", ["c"])},
... {"c": surv.Weibull.from_params([100, 2])},
... )
>>> longer = tree.with_levers({("c", "alpha"): 200.0})
>>> round(longer.sf(100), 4)
0.7788
sf_uncertainty ¶
sf_uncertainty(t=None, uncertainty=None, *, n_draws=1000, seed=None, sampling='random')
The probability that the top event has not occurred, over plausible event models (parameter uncertainty).
As NonRepairableRBD.sf_uncertainty does for a diagram's
reliability (see there for the kinds of uncertainty): each of the
n_draws draws gives every uncertain event a plausible model,
from the normal approximation of its surpyval fit ("fit"),
from distributions over named parameters, or from a list of
models, and the probability at t is worked out exactly for the
draw, with the common-cause groups (whose models may be drawn too,
the CCFGroup given as a key). The draws
are the diagram's of the tree (to_rbd, converted once): the
events are its nodes, so they are keyed as its nodes are, events
of one population together in a tuple, and a group's members
together. An event given as a probability has no fit, so it is
not drawn by default; its p may be given a distribution.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
t
|
array_like
|
Time/s, a number or an array. May be left out when every event's probability, drawn or not, is fixed. |
None
|
uncertainty
|
dict
|
|
None
|
n_draws
|
int
|
The number of draws, by default 1000. |
1000
|
seed
|
int
|
Seeds the draws, for reproducible results. |
None
|
sampling
|
str
|
|
'random'
|
Returns:
| Type | Description |
|---|---|
UncertaintyResult
|
The probability of every draw ( |
Raises:
| Type | Description |
|---|---|
ValueError
|
As for |
Examples:
Cooling is lost if both pumps fail or the valve does. The pumps are of one type, whose exponential failure rate is known only to lie between 1 and 3 per 1000 h, so they share the one draw:
>>> import scipy.stats as st
>>> import surpyval as surv
>>> from repyability import FaultTree
>>> pump = surv.Exponential.from_params([0.002])
>>> tree = FaultTree(
... {"no cooling": ("or", ["no flow", "valve"]),
... "no flow": ("and", ["pump 1", "pump 2"])},
... {"pump 1": pump, "pump 2": pump, "valve": 0.01},
... )
>>> rate = {"failure_rate": st.uniform(0.001, 0.002)}
>>> result = tree.sf_uncertainty(
... 500, {("pump 1", "pump 2"): rate}, n_draws=10_000, seed=1
... )
>>> round(result.nominal, 4)
0.5944
>>> lower, upper = result.interval(0.9)
>>> round(lower, 3), round(upper, 3)
(0.409, 0.813)
mean_uncertainty ¶
mean_uncertainty(uncertainty=None, *, n_draws=1000, seed=None, sampling='random')
The mean time to the top event over plausible event models (parameter uncertainty).
As sf_uncertainty does
for the probability: each draw gives every uncertain event a
plausible model, and the mean time to the top event is worked out
exactly for the draw, as mean
works it out. The draws are the diagram's of the tree (to_rbd,
converted once), as for sf_uncertainty.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
uncertainty
|
dict
|
|
None
|
n_draws
|
int
|
The number of draws, by default 1000. |
1000
|
seed
|
int
|
Seeds the draws, for reproducible results. |
None
|
sampling
|
str
|
|
'random'
|
Returns:
| Type | Description |
|---|---|
UncertaintyResult
|
The mean time of every draw ( |
Raises:
| Type | Description |
|---|---|
ValueError
|
As for |
NotImplementedError
|
With a common-cause group that splits a failure probability,
as for |
Examples:
Two pumps of one type, both of which must fail, whose exponential failure rate is known only to lie between 1 and 3 per 1000 h:
>>> import scipy.stats as st
>>> import surpyval as surv
>>> from repyability import FaultTree
>>> pump = surv.Exponential.from_params([0.002])
>>> tree = FaultTree(
... {"no flow": ("and", ["pump 1", "pump 2"])},
... {"pump 1": pump, "pump 2": pump},
... )
>>> rate = {"failure_rate": st.uniform(0.001, 0.002)}
>>> result = tree.mean_uncertainty(
... {("pump 1", "pump 2"): rate}, seed=1
... )
>>> round(result.nominal, 6) # 1.5 / 0.002
750.0
>>> lower, upper = result.interval(0.9)
>>> round(lower), round(upper)
(515, 1352)
analysis_routes ¶
analysis_routes()
How each analysis of this tree is computed, found without running it: exactly, numerically, by simulation, or not at all.
As a diagram's
analysis_routes:
the gates are always evaluated exactly, but a value is only as
exact as the events' probabilities it is made of, so an event
whose model's reliability is numerical makes the analyses built
on it numerical, and one with no reliability makes them refuse. A
refusal is found by the check the method itself runs, and its
reason is the message it would raise.
Returns:
| Type | Description |
|---|---|
dict[str, AnalysisRoute]
|
For each public analysis, by method name: its |
Examples:
A tree of probabilities has no time to the top event:
>>> from repyability import FaultTree
>>> tree = FaultTree(
... {"top": ("and", ["a", "b"])}, {"a": 0.1, "b": 0.2}
... )
>>> routes = tree.analysis_routes()
>>> routes["top_event_probability"].route
'exact'
>>> routes["mean"].route
'refused'
>>> routes["sf_uncertainty"].route
'simulated'
to_rbd ¶
to_rbd()
The reliability block diagram of the same system.
The diagram says when the system works: an OR gate becomes a series
block of its inputs, an AND gate a parallel block, and a VOTE gate
needing k of n failures a block needing n - k + 1 of its
n inputs working, through a perfect junction node. Each event
becomes a node with the event's model (a probability p becomes a
FixedEventProbability of p); junctions are
PerfectReliability nodes named after their gates.
An event or gate that feeds several gates is drawn once for each
place: an event's later appearances are repeated nodes (named
"event (2)", ...), which the diagram treats as the one
component. So every tree converts exactly.
Returns:
| Type | Description |
|---|---|
NonRepairableRBD
|
The diagram, from input node |
Examples:
>>> from repyability import FaultTree
>>> tree = FaultTree(
... {"top": ("or", ["valve", "flow"]),
... "flow": ("and", ["pump 1", "pump 2"])},
... {"pump 1": 0.1, "pump 2": 0.1, "valve": 0.05},
... )
>>> rbd = tree.to_rbd()
>>> round(rbd.sf(), 4), round(1 - tree.top_event_probability(), 4)
(0.9405, 0.9405)
from_rbd
classmethod
¶
from_rbd(rbd)
The fault tree of a reliability block diagram.
The top event is the system failing. The diagram's modules become
gates (a series block an OR gate, a parallel block an AND gate, a
block needing k of n members working a VOTE gate on
n - k + 1 failures), and a part that is not series-parallel
(e.g. a bridge) an OR gate over its minimal cut sets, each an AND
gate. The nodes' models become the events' (a node's failure by
time t is the event). Gates are named "TOP" and "G1",
"G2", ... (renamed if a node has that name). Nodes that cannot
affect the system are left out, but for a member of a common-cause
group with one that can: the tree keeps it, for the group's causes
to strike it with the others, as an event its logic makes
irrelevant (the top event OR(G, AND(G, member)), which is
G).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
rbd
|
NonRepairableRBD
|
The diagram. |
required |
Returns:
| Type | Description |
|---|---|
FaultTree
|
The tree: its top event probability is the diagram's unreliability. |
Raises:
| Type | Description |
|---|---|
TypeError
|
If |
ValueError
|
If the diagram cannot fail (a direct input-to-output edge). |
Warns:
| Type | Description |
|---|---|
UserWarning
|
If the diagram has node capacities, which the tree leaves out (a tree has no capacity analysis). |
Examples:
>>> from surpyval import FixedEventProbability
>>> from repyability import FaultTree, NonRepairableRBD
>>> rbd = NonRepairableRBD(
... [("s", "p1"), ("s", "p2"), ("p1", "v"), ("p2", "v"),
... ("v", "t")],
... {
... "p1": FixedEventProbability.from_params(0.1),
... "p2": FixedEventProbability.from_params(0.1),
... "v": FixedEventProbability.from_params(0.05),
... },
... )
>>> tree = FaultTree.from_rbd(rbd)
>>> tree.gates
{'G1': ('and', ['p1', 'p2']), 'TOP': ('or', ['G1', 'v'])}
>>> round(tree.top_event_probability(), 4), round(1 - rbd.sf(), 4)
(0.0595, 0.0595)
to_dict ¶
to_dict()
The tree as a JSON-compatible dict (see from_dict).
Returns:
| Type | Description |
|---|---|
dict
|
The gates, the events (probabilities as numbers, models as RePyability serialises them), the top event and any common-cause groups (as a diagram saves them). |
from_dict
classmethod
¶
from_dict(d)
Rebuild a tree from to_dict's output.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
d
|
dict
|
As |
required |
Returns:
| Type | Description |
|---|---|
FaultTree
|
The tree. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
to_json ¶
to_json(fp=None, **json_kwargs)
The tree as a JSON document (see to_dict): returned, or
written to fp (as surpyval's models' to_json(fp) writes
them).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fp
|
(str, PathLike or file)
|
A path, or a file opened for writing, to write the document to; by default None: it is returned. |
None
|
**json_kwargs
|
Passed to |
{}
|
Returns:
| Type | Description |
|---|---|
str or None
|
The JSON document, or None once written to |
PhasedMission ¶
PhasedMission(phases)
A mission through phases, each with its own duration and diagram over the same non-repairable components.
A mission (take-off, cruise, landing) runs through its phases in order.
Each phase is a NonRepairableRBD over some of the mission's
components, which it must keep working for the phase's duration. A
component is the same in every phase that uses it (the same node name,
with the same model), and its life runs through the whole mission: once
failed, it stays failed in the later phases, even those whose diagrams
tolerate it. So the phases are not independent, and the mission's
reliability is not the product of theirs. The mission succeeds if every
phase's diagram works to the phase's end.
reliability and phase_failure_probabilities are exact by
default: each component's life through the mission is a chain of
independent segments, one per phase (Esary and Ziehms, 1975), and the
phases' structures over those segments are combined in one binary
decision diagram, built from their diagrams' modules and cores
without listing path sets (see the module docstring), up to
MAX_NODES nodes. method="simulate" draws each component's life
once per mission
instead, and reliability_interval gives the simulated reliability
with a confidence interval, to a tolerance if asked. The importance
measures (birnbaum_importance and the others) hold a component
across every phase, working throughout or failed from the start, and
minimal_cut_sets gives the events, a component failed by the end
of a phase, that together fail the mission.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
phases
|
Sequence[tuple[Hashable, float, NonRepairableRBD]]
|
The phases in order, each a |
required |
Attributes:
| Name | Type | Description |
|---|---|---|
phases |
list
|
The phases, each with its |
components |
dict
|
Each component's model, by node name, in order of first use. |
duration |
float
|
The mission's length: the sum of the phases' durations. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Examples:
Two engines, either of which is enough to cruise, but both needed to take off, for 0.1 hours then 5:
>>> import surpyval as surv
>>> from repyability import NonRepairableRBD, PhasedMission
>>> engine = surv.Exponential.from_params([0.01])
>>> both = NonRepairableRBD(
... [("s", "e1"), ("e1", "e2"), ("e2", "t")],
... {"e1": engine, "e2": engine},
... )
>>> either = NonRepairableRBD(
... [("s", "e1"), ("s", "e2"), ("e1", "t"), ("e2", "t")],
... {"e1": engine, "e2": engine},
... )
>>> mission = PhasedMission(
... [("take-off", 0.1, both), ("cruise", 5.0, either)]
... )
>>> round(mission.reliability(), 6)
0.995628
reliability ¶
reliability(method='exact', *, mc_samples=None, seed=None)
The probability that the mission succeeds: that every phase's diagram works to the phase's end.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
method
|
str
|
|
'exact'
|
mc_samples
|
int
|
The number of simulated missions, by default 10_000
( |
None
|
seed
|
int
|
Seeds the simulation, by default None. |
None
|
Returns:
| Type | Description |
|---|---|
float
|
The mission's reliability. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
NotImplementedError
|
If the mission's decision diagram needs more than
|
Examples:
Two phases with the same diagram are one phase as long as both:
>>> import surpyval as surv
>>> from repyability import NonRepairableRBD, PhasedMission
>>> unit = surv.Weibull.from_params([100, 2])
>>> pair = NonRepairableRBD(
... [("s", "a"), ("s", "b"), ("a", "t"), ("b", "t")],
... {"a": unit, "b": unit},
... )
>>> mission = PhasedMission(
... [("first", 30.0, pair), ("second", 20.0, pair)]
... )
>>> round(mission.reliability(), 10) == round(float(pair.sf(50.0)), 10)
True
unreliability ¶
unreliability(method='exact', *, mc_samples=None, seed=None)
The probability that the mission fails, 1 - reliability(),
computed to its own relative precision when it is small (see
reliability for the parameters).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
method
|
str
|
|
'exact'
|
mc_samples
|
int
|
The number of simulated missions, by default 10_000. |
None
|
seed
|
int
|
Seeds the simulation, by default None. |
None
|
Returns:
| Type | Description |
|---|---|
float
|
The mission's unreliability. |
phase_failure_probabilities ¶
phase_failure_probabilities(method='exact', *, mc_samples=None, seed=None)
The probability that the mission fails in each phase: that the earlier phases get through and this one does not. They add up to the mission's unreliability.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
method
|
str
|
|
'exact'
|
mc_samples
|
int
|
The number of simulated missions, by default 10_000. |
None
|
seed
|
int
|
Seeds the simulation, by default None. |
None
|
Returns:
| Type | Description |
|---|---|
dict[Hashable, float]
|
Per phase, by name, the probability of failing in it. |
Examples:
>>> import surpyval as surv
>>> from repyability import NonRepairableRBD, PhasedMission
>>> unit = surv.Exponential.from_params([0.01])
>>> one = NonRepairableRBD([("s", "a"), ("a", "t")], {"a": unit})
>>> mission = PhasedMission([("out", 10.0, one), ("back", 10.0, one)])
>>> failing = mission.phase_failure_probabilities()
>>> {name: round(chance, 5) for name, chance in failing.items()}
{'out': 0.09516, 'back': 0.08611}
reliability_interval ¶
reliability_interval(mc_samples=10000, seed=None, *, confidence=0.95, tolerance=None, max_samples=None, antithetic=False)
The mission's reliability by simulation, with a confidence interval: each component's life drawn once per mission.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
mc_samples
|
int
|
The number of simulated missions, by default 10_000; with a
|
10000
|
seed
|
int
|
Seeds the simulation, by default None. |
None
|
confidence
|
float
|
The interval's confidence level, by default 0.95. |
0.95
|
tolerance
|
float
|
Simulate until the interval is at most |
None
|
max_samples
|
int
|
The most missions a run to |
None
|
antithetic
|
bool
|
Draw the missions in antithetic pairs, the second of each from
|
False
|
Returns:
| Type | Description |
|---|---|
ConfidenceInterval
|
The estimate, its standard error and a normal interval, clipped
to |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
NotImplementedError
|
If |
Examples:
>>> import surpyval as surv
>>> from repyability import NonRepairableRBD, PhasedMission
>>> unit = surv.Exponential.from_params([0.01])
>>> one = NonRepairableRBD([("s", "a"), ("a", "t")], {"a": unit})
>>> mission = PhasedMission([("out", 10.0, one), ("back", 10.0, one)])
>>> interval = mission.reliability_interval(20_000, seed=1)
>>> bool(interval.lower < mission.reliability() < interval.upper)
True
birnbaum_importance ¶
birnbaum_importance()
Birnbaum importance of each component over the mission.
B_i = R(i works) - R(i failed): the mission's reliability with
component i working throughout (every phase's segment of its
life survived) less its reliability with i failed from the
start (failed in the first phase, so in every phase), the
probability that i is critical to the mission. Each is the
mission's decision diagram replayed with the component's segments
so held, and the difference is taken from the probabilities of
succeeding or of failing, whichever are the smaller, so a small
importance in a reliable mission keeps its precision. Exact, at
the mission's end; it assumes independent components.
Returns:
| Type | Description |
|---|---|
dict[Hashable, float]
|
|
Raises:
| Type | Description |
|---|---|
NotImplementedError
|
If the mission's decision diagram needs more than |
Examples:
Two engines, both needed to take off and either to cruise: an
engine failed from the start fails the take-off, and one working
throughout carries the mission unless the other fails in the
take-off, so each is critical with the probability that the other
lasts the take-off, exp(-0.01 * 0.1).
>>> import surpyval as surv
>>> from repyability import NonRepairableRBD, PhasedMission
>>> engine = surv.Exponential.from_params([0.01])
>>> both = NonRepairableRBD(
... [("s", "e1"), ("e1", "e2"), ("e2", "t")],
... {"e1": engine, "e2": engine},
... )
>>> either = NonRepairableRBD(
... [("s", "e1"), ("s", "e2"), ("e1", "t"), ("e2", "t")],
... {"e1": engine, "e2": engine},
... )
>>> mission = PhasedMission(
... [("take-off", 0.1, both), ("cruise", 5.0, either)]
... )
>>> {k: round(v, 6) for k, v in mission.birnbaum_importance().items()}
{'e1': 0.999, 'e2': 0.999}
improvement_potential ¶
improvement_potential()
Improvement potential of each component over the mission.
IP_i = R(i works) - R: how much the mission's reliability would
rise if component i worked throughout (every phase's segment
of its life survived). Each is the mission's decision diagram
replayed with the component's segments so held (see
birnbaum_importance),
the difference taken from the probabilities of succeeding or of
failing, whichever are the smaller. Exact, at the mission's end; it
assumes independent components.
Returns:
| Type | Description |
|---|---|
dict[Hashable, float]
|
|
Raises:
| Type | Description |
|---|---|
NotImplementedError
|
If the mission's decision diagram needs more than |
Examples:
>>> import surpyval as surv
>>> from repyability import NonRepairableRBD, PhasedMission
>>> engine = surv.Exponential.from_params([0.01])
>>> both = NonRepairableRBD(
... [("s", "e1"), ("e1", "e2"), ("e2", "t")],
... {"e1": engine, "e2": engine},
... )
>>> either = NonRepairableRBD(
... [("s", "e1"), ("s", "e2"), ("e1", "t"), ("e2", "t")],
... {"e1": engine, "e2": engine},
... )
>>> mission = PhasedMission(
... [("take-off", 0.1, both), ("cruise", 5.0, either)]
... )
>>> potential = mission.improvement_potential()
>>> {k: round(v, 6) for k, v in potential.items()}
{'e1': 0.003372, 'e2': 0.003372}
risk_achievement_worth ¶
risk_achievement_worth()
Risk achievement worth (RAW) of each component over the mission.
RAW_i = F(i failed) / F, per Modarres & Kaminskiy: the factor by
which the mission's unreliability would grow if component i
were failed from the start (failed in the first phase, so in every
phase). It is at least 1. Each unreliability is the mission's
decision diagram replayed with the component's segments so held
(see
birnbaum_importance),
in its own right (not as one less a reliability), so a small one
keeps its precision; where the mission cannot fail the division
gives inf or nan. Exact, at the mission's end; it assumes
independent components.
Returns:
| Type | Description |
|---|---|
dict[Hashable, float]
|
|
Raises:
| Type | Description |
|---|---|
NotImplementedError
|
If the mission's decision diagram needs more than |
Examples:
Either engine failed from the start fails the take-off, so the mission: its unreliability becomes 1.
>>> import surpyval as surv
>>> from repyability import NonRepairableRBD, PhasedMission
>>> engine = surv.Exponential.from_params([0.01])
>>> both = NonRepairableRBD(
... [("s", "e1"), ("e1", "e2"), ("e2", "t")],
... {"e1": engine, "e2": engine},
... )
>>> either = NonRepairableRBD(
... [("s", "e1"), ("s", "e2"), ("e1", "t"), ("e2", "t")],
... {"e1": engine, "e2": engine},
... )
>>> mission = PhasedMission(
... [("take-off", 0.1, both), ("cruise", 5.0, either)]
... )
>>> raw = mission.risk_achievement_worth()
>>> {k: round(v, 2) for k, v in raw.items()}
{'e1': 228.74, 'e2': 228.74}
>>> round(1 / mission.unreliability(), 2)
228.74
risk_reduction_worth ¶
risk_reduction_worth()
Risk reduction worth (RRW) of each component over the mission.
RRW_i = F / F(i works), per Modarres & Kaminskiy: the factor by
which the mission's unreliability would shrink if component i
worked throughout (every phase's segment of its life survived). It
is at least 1, and inf where the component working throughout
makes the mission certain. Each unreliability is the mission's
decision diagram replayed with the component's segments so held
(see
birnbaum_importance),
in its own right, so a small one keeps its precision. Exact, at the
mission's end; it assumes independent components.
Returns:
| Type | Description |
|---|---|
dict[Hashable, float]
|
|
Raises:
| Type | Description |
|---|---|
NotImplementedError
|
If the mission's decision diagram needs more than |
Examples:
>>> import surpyval as surv
>>> from repyability import NonRepairableRBD, PhasedMission
>>> engine = surv.Exponential.from_params([0.01])
>>> both = NonRepairableRBD(
... [("s", "e1"), ("e1", "e2"), ("e2", "t")],
... {"e1": engine, "e2": engine},
... )
>>> either = NonRepairableRBD(
... [("s", "e1"), ("s", "e2"), ("e1", "t"), ("e2", "t")],
... {"e1": engine, "e2": engine},
... )
>>> mission = PhasedMission(
... [("take-off", 0.1, both), ("cruise", 5.0, either)]
... )
>>> rrw = mission.risk_reduction_worth()
>>> {k: round(v, 4) for k, v in rrw.items()}
{'e1': 4.374, 'e2': 4.374}
criticality_importance ¶
criticality_importance(kind='failure')
Criticality importance of each component over the mission.
With R and F the mission's reliability and unreliability,
and R(i works) and R(i failed) its reliability with
component i working throughout and failed from the start (see
birnbaum_importance):
kind="failure"(the default) gives the failure-oriented form (Rausand & Høyland),CI_i = (R(i works) - R) / F: the probability, given that the mission has failed, that it would have succeeded with componentiworking throughout; the share of the mission's failures thatiaccounts for. For a diagram this isB_i * (1 - R_i) / (1 - R_sys), ascriticality_importancegives it. It isnanwhere the mission cannot fail.kind="success"gives the success-oriented form,CI_i = (R - R(i failed)) / R: the probability, given that the mission succeeds, that it would have failed with componentifailed from the start (for a diagram,B_i * R_i / R_sys). It isnanwhere the mission cannot succeed.
Each difference is taken from the probabilities of succeeding or of failing, whichever are the smaller, so a small share keeps its precision. Exact, at the mission's end; it assumes independent components.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
kind
|
str
|
|
'failure'
|
Returns:
| Type | Description |
|---|---|
dict[Hashable, float]
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
NotImplementedError
|
If the mission's decision diagram needs more than |
Examples:
Two engines, both needed to take off and either to cruise: the
mission's failures without either engine's own failure are the
other's in the take-off, so each engine accounts for a share
1 - 1 / RRW_i of the failures.
>>> import surpyval as surv
>>> from repyability import NonRepairableRBD, PhasedMission
>>> engine = surv.Exponential.from_params([0.01])
>>> both = NonRepairableRBD(
... [("s", "e1"), ("e1", "e2"), ("e2", "t")],
... {"e1": engine, "e2": engine},
... )
>>> either = NonRepairableRBD(
... [("s", "e1"), ("s", "e2"), ("e1", "t"), ("e2", "t")],
... {"e1": engine, "e2": engine},
... )
>>> mission = PhasedMission(
... [("take-off", 0.1, both), ("cruise", 5.0, either)]
... )
>>> ci = mission.criticality_importance()
>>> {k: round(v, 4) for k, v in ci.items()}
{'e1': 0.7714, 'e2': 0.7714}
>>> round(1 - 1 / mission.risk_reduction_worth()["e1"], 4)
0.7714
fussell_vesely ¶
fussell_vesely(method='exact')
Fussell-Vesely importance of each component over the mission:
the share of the mission's failure probability that involves the
component, through the mission's minimal cut sets (see
minimal_cut_sets),
sets of events "component c has failed by the end of phase
j", each with probability F_c(T_j), that together fail the
mission. A cut set has occurred when every one of its events has,
and a component's are the cut sets naming it in any phase:
FV_i = P(some minimal cut set naming i has occurred) / F
with F the mission's unreliability. method="exact" (the
default) works out the probability of that union exactly, from a
decision diagram of the cut sets' events over the components'
segments (an event being the component's first j + 1 segments
not all survived), so it is between 0 and 1.
method="rare_event" gives the rare-event approximation that
many PRA tools report, summing the cut sets' probabilities, each
the product of its events', instead of taking their union:
FV_i = sum over minimal cut sets C naming i of
prod over (c, j) in C of F_c(T_j), divided by F
The sum over-estimates the union, so it can exceed 1 when the
failure probabilities are not small. F and each event's
probability are worked out in their own right (the mission's as
unreliability is, a
component's from its model's ff), and so is the union, so
small ones keep their precision. Exact; it assumes independent
components.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
method
|
str
|
|
'exact'
|
Returns:
| Type | Description |
|---|---|
dict[Hashable, float]
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
NotImplementedError
|
If the mission's decision diagram, or with |
Examples:
Two engines, both needed to take off and either to cruise: an engine's share is the failures through its own loss in the take-off or both engines' by the end of the cruise, not through the other's loss in the take-off alone. The rare-event sum counts a failure that is both at once twice.
>>> import surpyval as surv
>>> from repyability import NonRepairableRBD, PhasedMission
>>> engine = surv.Exponential.from_params([0.01])
>>> both = NonRepairableRBD(
... [("s", "e1"), ("e1", "e2"), ("e2", "t")],
... {"e1": engine, "e2": engine},
... )
>>> either = NonRepairableRBD(
... [("s", "e1"), ("s", "e2"), ("e1", "t"), ("e2", "t")],
... {"e1": engine, "e2": engine},
... )
>>> mission = PhasedMission(
... [("take-off", 0.1, both), ("cruise", 5.0, either)]
... )
>>> fv = mission.fussell_vesely()
>>> {k: round(v, 4) for k, v in fv.items()}
{'e1': 0.7827, 'e2': 0.7827}
>>> fv = mission.fussell_vesely(method="rare_event")
>>> {k: round(v, 4) for k, v in fv.items()}
{'e1': 0.7941, 'e2': 0.7941}
minimal_cut_sets ¶
minimal_cut_sets()
The mission's minimal cut sets: sets of events (component,
phase name), "the component has failed by the end of that
phase", that together fail the mission, smallest first (then by
name).
A failure by one phase is one by every later phase, so an event is implied by the same component's at an earlier phase. A cut set is minimal when no event can be dropped and none replaced by a weaker one, the component's failure by a later phase. They are the phases' own minimal cut sets (each a phase's diagram's, its components' failures dated to that phase), less those another implies: a set is dropped when every event of another follows from one of its own (the other names no component it lacks, each at that phase or a later one), the usual phased-mission minimisation. A phase whose diagram can never work has the empty cut set, which implies every other. They are worked out on first use and kept.
Returns:
| Type | Description |
|---|---|
list of frozenset
|
The minimal cut sets, each a frozenset of |
Raises:
| Type | Description |
|---|---|
NotImplementedError
|
If a phase's diagram is too meshed for its cut sets. |
Examples:
Two engines, both needed to take off and either to cruise: an engine failed by the end of the take-off fails the mission, as do both failed by the end of the cruise.
>>> import surpyval as surv
>>> from repyability import NonRepairableRBD, PhasedMission
>>> engine = surv.Exponential.from_params([0.01])
>>> both = NonRepairableRBD(
... [("s", "e1"), ("e1", "e2"), ("e2", "t")],
... {"e1": engine, "e2": engine},
... )
>>> either = NonRepairableRBD(
... [("s", "e1"), ("s", "e2"), ("e1", "t"), ("e2", "t")],
... {"e1": engine, "e2": engine},
... )
>>> mission = PhasedMission(
... [("take-off", 0.1, both), ("cruise", 5.0, either)]
... )
>>> for cut in mission.minimal_cut_sets():
... print(sorted(cut))
[('e1', 'take-off')]
[('e2', 'take-off')]
[('e1', 'cruise'), ('e2', 'cruise')]
analysis_routes ¶
analysis_routes()
How each analysis of the mission is computed, found without running it: exactly, by simulation, or not at all.
The exact analyses replay the phases' decision diagram over the
components' segments, and refuse when it would need more than
MAX_NODES nodes (the diagram is built, and kept for the
analyses; the Fussell-Vesely importance's cut sets' diagram too).
They are as exact as the components' reliabilities: a component
whose reliability is numerical makes them numerical, and one with
none (a StandbyModel only simulations take) makes them refuse,
as the diagrams' routes have it. The refusal is the message the
method would raise.
Returns:
| Type | Description |
|---|---|
dict[str, AnalysisRoute]
|
For each public analysis, by method name: its |
Examples:
>>> import surpyval as surv
>>> from repyability import NonRepairableRBD, PhasedMission
>>> unit = surv.Exponential.from_params([0.01])
>>> one = NonRepairableRBD([("s", "a"), ("a", "t")], {"a": unit})
>>> mission = PhasedMission([("out", 10.0, one), ("back", 10.0, one)])
>>> routes = mission.analysis_routes()
>>> routes["reliability"].route, routes["reliability_interval"].route
('exact', 'simulated')
Network ¶
Network(links, source, target, nodes=None)
An undirected network whose links, and optionally nodes, fail, and the reliability of the connection between two of its nodes.
The network works while some path of working links, through working
nodes, joins source to target. A link works both ways. Nodes
work for ever unless given a model in nodes; the terminals can be
given one too. Every element fails independently of the others.
The exact values come from a binary decision diagram built from the
network, link by link, whose size grows with the network's width
rather than with its number of paths (see the module docstring): a
grid of 100 nodes takes a second and a half. One of more than
MAX_STATES (5,000,000) states refuses, pointing to the
simulation. The importance measures are those of a
NonRepairableRBD, over the links and
the failing nodes; analysis_routes
says how each analysis is worked out, and
to_dict saves the network.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
links
|
dict
|
Each link's name, mapped to |
required |
source
|
Hashable
|
One terminal: a node of the network. |
required |
target
|
Hashable
|
The other terminal, another node. |
required |
nodes
|
dict
|
The nodes that can fail, each mapped to its model (or the probability that it has failed), by default none. Their names must differ from the links'. |
None
|
Attributes:
| Name | Type | Description |
|---|---|---|
links |
dict
|
Each link's two nodes, by name. |
models |
dict
|
Each element's model, by name: the links', then the nodes'. |
source, target |
Hashable
|
The terminals. |
is_fixed |
bool
|
Whether every model is a fixed probability (time is then
irrelevant, and |
Raises:
| Type | Description |
|---|---|
ValueError
|
If there is no link, a link is not a |
Examples:
The bridge network: links a and b out of the source, d and
e into the target, and c across the middle, which works either
way. With every link working with probability 0.9, the terminals stay
connected with probability 2p^2 + 2p^3 - 5p^4 + 2p^5:
>>> from surpyval import FixedEventProbability
>>> from repyability import Network
>>> link = FixedEventProbability.from_params(0.1)
>>> bridge = Network(
... {
... "a": ("s", "x", link),
... "b": ("s", "y", link),
... "c": ("x", "y", link),
... "d": ("x", "t", link),
... "e": ("y", "t", link),
... },
... source="s",
... target="t",
... )
>>> round(bridge.sf(), 5)
0.97848
>>> sorted(sorted(p) for p in bridge.path_sets())
[['a', 'c', 'e'], ['a', 'd'], ['b', 'c', 'd'], ['b', 'e']]
path_sets ¶
path_sets()
The minimal path sets, as a set: for each simple path between
the terminals, the set of its links and of its nodes that can fail
(minimal_path_sets
lists them, smallest first).
Returns:
| Type | Description |
|---|---|
set[frozenset]
|
The minimal path sets, each a frozenset of link and node names. |
Raises:
| Type | Description |
|---|---|
NotImplementedError
|
If there are more than |
cut_sets ¶
cut_sets()
The minimal cut sets, as a set: the smallest sets of links and
nodes whose failure parts the terminals
(minimal_cut_sets lists
them, smallest first).
Returns:
| Type | Description |
|---|---|
set[frozenset]
|
The minimal cut sets, each a frozenset of link and node names. |
Raises:
| Type | Description |
|---|---|
NotImplementedError
|
If the network's decision diagram would have more than
|
minimal_path_sets ¶
minimal_path_sets()
The minimal path sets, listed smallest first (then by name), as
a diagram's minimal_path_sets
and a tree's list them: the sets of path_sets().
Returns:
| Type | Description |
|---|---|
list[frozenset]
|
The minimal path sets, each a frozenset of link and node names. |
Raises:
| Type | Description |
|---|---|
NotImplementedError
|
As for |
Examples:
>>> from repyability import Network
>>> fork = Network(
... {"a": ("s", "x", 0.1), "b": ("x", "t", 0.1),
... "c": ("s", "t", 0.2)},
... "s", "t",
... )
>>> [sorted(path) for path in fork.minimal_path_sets()]
[['c'], ['a', 'b']]
minimal_cut_sets ¶
minimal_cut_sets()
The minimal cut sets, listed smallest first (then by name), as
a diagram's minimal_cut_sets
and a tree's list them: the sets of cut_sets().
Returns:
| Type | Description |
|---|---|
list[frozenset]
|
The minimal cut sets, each a frozenset of link and node names. |
Raises:
| Type | Description |
|---|---|
NotImplementedError
|
As for |
Examples:
>>> from repyability import Network
>>> fork = Network(
... {"a": ("s", "x", 0.1), "b": ("x", "t", 0.1),
... "c": ("s", "t", 0.2)},
... "s", "t",
... )
>>> [sorted(cut) for cut in fork.minimal_cut_sets()]
[['a', 'c'], ['b', 'c']]
sf ¶
sf(x=None, method='exact', *, mc_samples=None, seed=None)
The probability that the terminals are connected at time/s
x.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
float or array_like
|
The time/s; may be left out if every model is a fixed probability. |
None
|
method
|
str
|
|
'exact'
|
mc_samples
|
int
|
The number of simulated networks, by default 10_000. |
None
|
seed
|
int
|
Seeds the simulation, by default None. |
None
|
Returns:
| Type | Description |
|---|---|
float or ndarray
|
The reliability: a float for a scalar |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
NotImplementedError
|
With |
ff ¶
ff(x=None, method='exact', *, mc_samples=None, seed=None)
The probability that the terminals are parted at time/s x,
1 - sf(x), worked out to its own precision when it is small
(see sf for the parameters).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
float or array_like
|
The time/s. |
None
|
method
|
str
|
|
'exact'
|
mc_samples
|
int
|
The number of simulated networks, by default 10_000. |
None
|
seed
|
int
|
Seeds the simulation, by default None. |
None
|
Returns:
| Type | Description |
|---|---|
float or ndarray
|
The unreliability. |
birnbaum_importance ¶
birnbaum_importance(x=None)
Each element's Birnbaum importance at time/s x: the
reliability with it working less that with it failed.
Every element's at once, as the derivative of the reliability, by one pass forward through the plan and one back, from whichever of the reliability and the unreliability is the smaller, so that a small importance keeps its precision.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
float or array_like
|
The time/s. |
None
|
Returns:
| Type | Description |
|---|---|
dict
|
Per link and failing node, by name, its importance (0 for one that is in no path). |
Raises:
| Type | Description |
|---|---|
(ValueError, NotImplementedError)
|
As for |
improvement_potential ¶
improvement_potential(x=None)
Each element's improvement potential at time/s x: how much
the reliability would rise if it were made perfect.
IP_i = R(i working) - R, worked out as the Birnbaum importance
times the element's probability of failing (its model's ff),
so that a small one keeps its precision; as
NonRepairableRBD.improvement_potential
defines it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
float or array_like
|
The time/s; may be left out if every model is a fixed probability. |
None
|
Returns:
| Type | Description |
|---|---|
dict
|
Per link and failing node, by name, its improvement potential:
floats for a scalar |
Raises:
| Type | Description |
|---|---|
(ValueError, NotImplementedError)
|
As for |
Examples:
>>> from repyability import Network
>>> bridge = Network(
... {"a": ("s", "x", 0.1), "b": ("s", "y", 0.1),
... "c": ("x", "y", 0.1), "d": ("x", "t", 0.1),
... "e": ("y", "t", 0.1)},
... "s", "t",
... )
>>> round(bridge.improvement_potential()["a"], 5)
0.01062
risk_achievement_worth ¶
risk_achievement_worth(x=None)
Each element's risk achievement worth (RAW) at time/s x: the
factor by which the unreliability would grow if it were failed.
RAW_i = (1 - R(i failed)) / (1 - R), each unreliability worked
out in its own right from the decision diagram with the element
held failed, as
NonRepairableRBD.risk_achievement_worth
defines it. It is at least 1; inf or nan, with a numpy
RuntimeWarning, where the unreliability is 0.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
float or array_like
|
The time/s; may be left out if every model is a fixed probability. |
None
|
Returns:
| Type | Description |
|---|---|
dict
|
Per link and failing node, by name, its RAW: floats for a
scalar |
Raises:
| Type | Description |
|---|---|
(ValueError, NotImplementedError)
|
As for |
Examples:
>>> from repyability import Network
>>> bridge = Network(
... {"a": ("s", "x", 0.1), "b": ("s", "y", 0.1),
... "c": ("x", "y", 0.1), "d": ("x", "t", 0.1),
... "e": ("y", "t", 0.1)},
... "s", "t",
... )
>>> round(bridge.risk_achievement_worth()["a"], 4)
5.4414
risk_reduction_worth ¶
risk_reduction_worth(x=None)
Each element's risk reduction worth (RRW) at time/s x: the
factor by which the unreliability would shrink if it were made
perfect.
RRW_i = (1 - R) / (1 - R(i working)), each unreliability worked
out in its own right from the decision diagram with the element
held working, as
NonRepairableRBD.risk_reduction_worth
defines it. It is at least 1, and inf where a perfect element makes
the network perfect (a link that alone joins the terminals).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
float or array_like
|
The time/s; may be left out if every model is a fixed probability. |
None
|
Returns:
| Type | Description |
|---|---|
dict
|
Per link and failing node, by name, its RRW: floats for a
scalar |
Raises:
| Type | Description |
|---|---|
(ValueError, NotImplementedError)
|
As for |
Examples:
>>> from repyability import Network
>>> bridge = Network(
... {"a": ("s", "x", 0.1), "b": ("s", "y", 0.1),
... "c": ("x", "y", 0.1), "d": ("x", "t", 0.1),
... "e": ("y", "t", 0.1)},
... "s", "t",
... )
>>> round(bridge.risk_reduction_worth()["a"], 4)
1.9743
criticality_importance ¶
criticality_importance(x=None, kind='failure')
Each element's criticality importance at time/s x.
With B_i the Birnbaum importance, R_i the element's
reliability and R the network's, kind="failure" (the
default) gives CI_i = B_i (1 - R_i) / (1 - R): the probability
that the element has failed and is critical, given that the
terminals are parted, the share of the network's failures it
accounts for; kind="success" gives CI_i = B_i R_i / R, the
probability that it is working and critical, given that the
terminals are joined. Each is worked out from the Birnbaum
importance and the network's unreliability (or reliability) in
their own right, so a small one keeps its precision; nan where the
network cannot fail (or cannot work). As
NonRepairableRBD.criticality_importance
defines it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
float or array_like
|
The time/s; may be left out if every model is a fixed probability. |
None
|
kind
|
str
|
|
'failure'
|
Returns:
| Type | Description |
|---|---|
dict
|
Per link and failing node, by name, its criticality importance:
floats for a scalar |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
NotImplementedError
|
As for |
Examples:
>>> from repyability import Network
>>> bridge = Network(
... {"a": ("s", "x", 0.1), "b": ("s", "y", 0.1),
... "c": ("x", "y", 0.1), "d": ("x", "t", 0.1),
... "e": ("y", "t", 0.1)},
... "s", "t",
... )
>>> round(bridge.criticality_importance()["a"], 4)
0.4935
fussell_vesely ¶
fussell_vesely(x=None, fv_type='c', method='exact')
Each element's Fussell-Vesely importance at time/s x: the
share of the network's failure probability that involves it.
With fv_type="c" (the default), the probability that some
minimal cut set containing the element has failed (every element
in it), over the probability that the terminals are parted; with
fv_type="p", the minimal path sets take the cut sets' place.
method="exact" (the default) works out the union exactly, from
a decision diagram of each element's sets (built once), as
NonRepairableRBD.fussell_vesely
does; "rare_event" sums the sets' probabilities instead, the
rare-event approximation, which can exceed 1 when failures are not
rare. Each probability is worked out in its own right, so a small
one keeps its precision; nan where the network cannot fail.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
float or array_like
|
The time/s; may be left out if every model is a fixed probability. |
None
|
fv_type
|
str
|
|
'c'
|
method
|
str
|
|
'exact'
|
Returns:
| Type | Description |
|---|---|
dict
|
Per link and failing node, by name, its Fussell-Vesely
importance: floats for a scalar |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
NotImplementedError
|
As for |
Examples:
>>> from repyability import Network
>>> bridge = Network(
... {"a": ("s", "x", 0.1), "b": ("s", "y", 0.1),
... "c": ("x", "y", 0.1), "d": ("x", "t", 0.1),
... "e": ("y", "t", 0.1)},
... "s", "t",
... )
>>> round(bridge.fussell_vesely()["a"], 4)
0.5065
>>> round(bridge.fussell_vesely(method="rare_event")["a"], 4)
0.5112
structural_importance ¶
structural_importance()
Each element's structural importance: the fraction of the other elements' states in which it is pivotal (the terminals joined when it works and parted when it fails), from the structure alone.
It is the Birnbaum importance with every element's probability at
1/2, as
RBD.structural_importance
works it out, and does not depend on the models.
Returns:
| Type | Description |
|---|---|
dict
|
Per link and failing node, by name, its structural importance,
in |
Raises:
| Type | Description |
|---|---|
NotImplementedError
|
If the network's decision diagram would have more than
|
Examples:
The bridge's middle link is pivotal in an eighth of the other links' states, each other link in three eighths:
>>> from repyability import Network
>>> bridge = Network(
... {"a": ("s", "x", 0.1), "b": ("s", "y", 0.1),
... "c": ("x", "y", 0.1), "d": ("x", "t", 0.1),
... "e": ("y", "t", 0.1)},
... "s", "t",
... )
>>> structural = bridge.structural_importance()
>>> structural["c"], structural["a"]
(0.125, 0.375)
differential_importance ¶
differential_importance(x=None, *, change='uniform', kind='failure', groups=None)
Each element's share of the change in the reliability when they
all change together: the differential importance measure (DIM,
Borgonovo & Apostolakis, 2001), as
NonRepairableRBD.differential_importance
defines it over the components.
change="uniform" moves every element's probability by as much,
and shares out the Birnbaum importance; "proportional" each by
the same fraction of itself, and shares out the criticality
importance of that kind. The shares add up to 1, and a group's
share is the sum of its members' (groups). NaN where there is
nothing to share out.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
float or array_like
|
The time/s; may be left out if every model is a fixed probability. |
None
|
change
|
str
|
|
'uniform'
|
kind
|
str
|
With a proportional change, which of each element's
probabilities moves in proportion: |
'failure'
|
groups
|
dict
|
|
None
|
Returns:
| Type | Description |
|---|---|
dict
|
The shares, by element or group name: floats for a scalar
|
Raises:
| Type | Description |
|---|---|
ValueError
|
For an unknown |
NotImplementedError
|
As for |
Examples:
>>> from repyability import Network
>>> bridge = Network(
... {"a": ("s", "x", 0.1), "b": ("s", "y", 0.1),
... "c": ("x", "y", 0.1), "d": ("x", "t", 0.1),
... "e": ("y", "t", 0.1)},
... "s", "t",
... )
>>> shares = bridge.differential_importance(
... groups={"ends": ["a", "b", "d", "e"], "middle": ["c"]}
... )
>>> {name: round(share, 4) for name, share in shares.items()}
{'ends': 0.9633, 'middle': 0.0367}
joint_importance ¶
joint_importance(x=None)
The joint (second-order) importance of each pair of elements:
whether improving the two together is worth more than improving
each (Hong & Lie, 1993), as
NonRepairableRBD.joint_importance
defines it.
JRI(i, j) = R(1_i, 1_j) - R(1_i, 0_j) - R(0_i, 1_j) + R(0_i,
0_j): how much element j's Birnbaum importance rises when
element i goes from failed to working, worked out as that
difference from the decision diagram with i held. Positive,
the two are complements (links in a chain); negative, substitutes
(links side by side); 0 where neither changes what the other is
worth.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
float or array_like
|
The time/s; may be left out if every model is a fixed probability. |
None
|
Returns:
| Type | Description |
|---|---|
dict
|
|
Raises:
| Type | Description |
|---|---|
(ValueError, NotImplementedError)
|
As for |
Examples:
>>> from repyability import Network
>>> bridge = Network(
... {"a": ("s", "x", 0.1), "b": ("s", "y", 0.1),
... "c": ("x", "y", 0.1), "d": ("x", "t", 0.1),
... "e": ("y", "t", 0.1)},
... "s", "t",
... )
>>> joint = bridge.joint_importance()
>>> round(joint["a", "d"], 4), round(joint["a", "b"], 4)
(0.028, -0.972)
barlow_proschan_importance ¶
barlow_proschan_importance(x=None)
Each element's Barlow-Proschan importance: the probability that
the network's failure is caused by the element's, given that it
fails by x, or over its whole life, as
NonRepairableRBD.barlow_proschan_importance
defines it.
An element's failure parts the terminals when the element is
critical then, so the network has failed by x through element
i with probability integral_0^x B_i(t) f_i(t) dt, its
Birnbaum importance times its failure density (its model's df,
or differences of its ff), integrated by adaptive quadrature
(to about 1e-9, relative); its share of the sum over the
elements is its importance, and the shares add up to 1.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
float or array_like
|
The time/s by which the network has failed, by default its whole life. |
None
|
Returns:
| Type | Description |
|---|---|
dict
|
Per link and failing node, by name, its share: floats for a
scalar |
Raises:
| Type | Description |
|---|---|
ValueError
|
For a negative time, or if every element is a fixed probability (the network then has no lifetimes). A fixed element among others has no density, so no share: it has failed from the start, or never does. |
NotImplementedError
|
As for |
Examples:
Two links in a chain, with failure rates 1 and 3: the second causes three quarters of the failures.
>>> import surpyval as surv
>>> from repyability import Network
>>> E = surv.Exponential.from_params
>>> chain = Network(
... {"a": (0, 1, E([1.0])), "b": (1, 2, E([3.0]))}, 0, 2
... )
>>> shares = chain.barlow_proschan_importance()
>>> {name: round(share, 6) for name, share in shares.items()}
{'a': 0.25, 'b': 0.75}
time_to_reliability ¶
time_to_reliability(target, upper_bound=None)
The time at which the reliability falls to target: the
inverse of sf, by root-finding, as
NonRepairableRBD.time_to_reliability
finds it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
target
|
float or array_like
|
The reliability to solve for, in (0, 1), or several. |
required |
upper_bound
|
float
|
A time at which the reliability is below |
None
|
Returns:
| Type | Description |
|---|---|
float or ndarray
|
The time at which |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
NotImplementedError
|
As for |
Examples:
>>> import surpyval as surv
>>> from repyability import Network
>>> chain = Network(
... {"a": (0, 1, surv.Weibull.from_params([100, 2]))}, 0, 1
... )
>>> round(chain.time_to_reliability(0.9), 2)
32.46
bx_life ¶
bx_life(x, upper_bound=None)
The Bx life: the time by which x percent of networks have
failed, time_to_reliability(1 - x / 100).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
float or array_like
|
The percentage failed (10 for the B10 life, not a fraction), in (0, 100), or several. |
required |
upper_bound
|
float
|
As for |
None
|
Returns:
| Type | Description |
|---|---|
float or ndarray
|
The time by which |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
NotImplementedError
|
As for |
Examples:
>>> import surpyval as surv
>>> from repyability import Network
>>> chain = Network(
... {"a": (0, 1, surv.Weibull.from_params([100, 2]))}, 0, 1
... )
>>> round(chain.bx_life(10), 2)
32.46
sf_uncertainty ¶
sf_uncertainty(x=None, uncertainty=None, *, n_draws=1000, seed=None, sampling='random')
The reliability over plausible models of the links and nodes
(parameter uncertainty), as
NonRepairableRBD.sf_uncertainty
gives a diagram's.
Each of the n_draws draws gives every uncertain element a
plausible model, and the reliability at x is worked out
exactly for that draw. An element's uncertainty is "fit" (its
parameters drawn from the normal approximation of its surpyval
fit, through its covariance), {parameter name: distribution},
or a list of models drawn with replacement; elements given together
in a tuple share their draws, as links of one type fitted to one
set of data should.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
float or array_like
|
The time/s; may be left out if every model, and every drawn model, is a fixed probability. |
None
|
uncertainty
|
dict
|
|
None
|
n_draws
|
int
|
The number of draws, by default 1000. |
1000
|
seed
|
int
|
Seeds the draws, by default None. |
None
|
sampling
|
str
|
|
'random'
|
Returns:
| Type | Description |
|---|---|
UncertaintyResult
|
The reliability of every draw ( |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
NotImplementedError
|
As for |
Examples:
A chain of two lines of one type, whose failure rate is known only to lie between 0.05 and 0.15 a year, drawn once for both:
>>> import scipy.stats as st
>>> import surpyval as surv
>>> from repyability import Network
>>> line = surv.Exponential.from_params([0.1])
>>> chain = Network({"a": (0, 1, line), "b": (1, 2, line)}, 0, 2)
>>> rate = {"failure_rate": st.uniform(0.05, 0.1)}
>>> result = chain.sf_uncertainty(
... 1.0, {("a", "b"): rate}, n_draws=10_000, seed=1
... )
>>> round(result.nominal, 4)
0.8187
>>> lower, upper = result.interval(0.9)
>>> round(lower, 3), round(upper, 3)
(0.748, 0.896)
mean_uncertainty ¶
mean_uncertainty(uncertainty=None, *, n_draws=1000, seed=None, sampling='random')
The mean time until the terminals are parted over plausible
models of the links and nodes (parameter uncertainty), as
NonRepairableRBD.mean_uncertainty
gives a diagram's: each draw's mean worked out as mean works it
out, the area under its exact reliability.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
uncertainty
|
dict
|
As for |
None
|
n_draws
|
int
|
The number of draws, by default 1000. |
1000
|
seed
|
int
|
Seeds the draws, by default None. |
None
|
sampling
|
str
|
|
'random'
|
Returns:
| Type | Description |
|---|---|
UncertaintyResult
|
The mean lifetime of every draw ( |
Raises:
| Type | Description |
|---|---|
ValueError
|
As for |
NotImplementedError
|
As for |
Examples:
>>> import scipy.stats as st
>>> import surpyval as surv
>>> from repyability import Network
>>> line = surv.Exponential.from_params([0.1])
>>> chain = Network({"a": (0, 1, line), "b": (1, 2, line)}, 0, 2)
>>> rate = {"failure_rate": st.uniform(0.05, 0.1)}
>>> result = chain.mean_uncertainty({("a", "b"): rate}, seed=1)
>>> round(result.nominal, 6) # 1 / 0.2
5.0
>>> lower, upper = result.interval(0.9)
>>> round(lower, 2), round(upper, 2)
(3.43, 9.01)
mean ¶
mean(method='exact', *, mc_samples=None, seed=None)
The mean time until the terminals are parted.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
method
|
str
|
|
'exact'
|
mc_samples
|
int
|
The number of simulated networks, by default 10_000. |
None
|
seed
|
int
|
Seeds the simulation, by default None. |
None
|
Returns:
| Type | Description |
|---|---|
float
|
The mean lifetime: |
Raises:
| Type | Description |
|---|---|
ValueError
|
If every model is a fixed probability (the network then has
no lifetimes), |
random ¶
random(size, seed=None)
size random lifetimes of the connection: in each, every
element's lifetime is drawn, and the connection lasts as long as
its longest-lasting path (whose life is that of its first element
to fail).
The path is found by adding links in order of their lives, longest first, until the terminals are joined: a link lasts no longer than the nodes it joins.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
size
|
int
|
The number of lifetimes. |
required |
seed
|
int
|
Seeds the draws, by default None. |
None
|
Returns:
| Type | Description |
|---|---|
ndarray
|
The lifetimes ( |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
to_dict ¶
to_dict()
The network as a JSON-friendly dict (see from_dict), as a
diagram saves its nodes (see
RBD.to_dict).
Returns:
| Type | Description |
|---|---|
dict
|
The |
Raises:
| Type | Description |
|---|---|
NotImplementedError
|
If a model cannot be serialised: one that is not surpyval's (nor a RePyability node model). |
Examples:
>>> import surpyval as surv
>>> from repyability import Network
>>> chain = Network(
... {"a": (0, 1, surv.Weibull.from_params([100, 2]))}, 0, 1
... )
>>> d = chain.to_dict()
>>> d["type"], d["links"][0]["nodes"]
('Network', [0, 1])
from_dict
classmethod
¶
from_dict(d)
Rebuild a network from to_dict's output, by calling the
constructor again, so it is checked again. Tuple names (a grid's
(i, j)), which JSON turns into lists, are turned back.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
d
|
dict
|
As |
required |
Returns:
| Type | Description |
|---|---|
Network
|
The network. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Examples:
>>> import surpyval as surv
>>> from repyability import Network
>>> chain = Network(
... {"a": (0, 1, surv.Weibull.from_params([100, 2]))}, 0, 1
... )
>>> Network.from_dict(chain.to_dict()).sf(50) == chain.sf(50)
True
to_json ¶
to_json(fp=None, **json_kwargs)
The network as a JSON document (see to_dict): returned, or
written to fp (as surpyval's models' to_json(fp) writes
them).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fp
|
(str, PathLike or file)
|
A path, or a file opened for writing, to write the document to; by default None: it is returned. |
None
|
**json_kwargs
|
Passed to |
{}
|
Returns:
| Type | Description |
|---|---|
str or None
|
The JSON document, or None once written to |
Raises:
| Type | Description |
|---|---|
NotImplementedError
|
If a model cannot be serialised (see |
TypeError
|
If a name cannot be encoded as JSON. |
from_json
classmethod
¶
from_json(s)
Rebuild a network from to_json's output.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
s
|
(str, PathLike or file)
|
The JSON document: its text, a path to a file holding it, or a file opened for reading. |
required |
Returns:
| Type | Description |
|---|---|
Network
|
The network. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
FileNotFoundError
|
If |
Examples:
>>> import surpyval as surv
>>> from repyability import Network
>>> chain = Network(
... {"a": (0, 1, surv.Weibull.from_params([100, 2]))}, 0, 1
... )
>>> round(Network.from_json(chain.to_json()).sf(50), 4)
0.7788
analysis_routes ¶
analysis_routes()
How each analysis of this network is computed, found without
running it: exactly, numerically, by simulation, or not at all (as
NonRepairableRBD.analysis_routes
reports a diagram's).
The exact values come from the network's decision diagram, so a
network of more than MAX_STATES states refuses every analysis
but the simulation (and the paths listed), with the message the
methods raise; an element whose model has no reliability of its
own makes the analyses built on it refuse, as it does.
Returns:
| Type | Description |
|---|---|
dict[str, AnalysisRoute]
|
For each public analysis, by method name: its |
Examples:
>>> from repyability import Network
>>> bridge = Network(
... {"a": ("s", "x", 0.1), "b": ("s", "y", 0.1),
... "c": ("x", "y", 0.1), "d": ("x", "t", 0.1),
... "e": ("y", "t", 0.1)},
... "s", "t",
... )
>>> routes = bridge.analysis_routes()
>>> routes["fussell_vesely"].route, routes["random"].route
('exact', 'simulated')
>>> routes["mean"].route # a fixed probability has no lifetime
'refused'