Nodes
Index
EnhancedBayesianNetworks.ApproximatedDiscretizationEnhancedBayesianNetworks.ContinuousFunctionalNodeEnhancedBayesianNetworks.ContinuousNodeEnhancedBayesianNetworks.DiscreteFunctionalNodeEnhancedBayesianNetworks.DiscreteNodeEnhancedBayesianNetworks.ExactDiscretizationEnhancedBayesianNetworks.ispreciseEnhancedBayesianNetworks.isrootEnhancedBayesianNetworks.parentsEnhancedBayesianNetworks.sampleEnhancedBayesianNetworks.scenariosEnhancedBayesianNetworks.states
Types
EnhancedBayesianNetworks.DiscreteNode Type
DiscreteNode(name, parents=Symbol[], parameters=[], results=nothing)
DiscreteNode(cpt::ScenariosTable, parameters=[], results=nothing)A discrete-state node. Holds a conditional probability table (cpt) over its parents' state combinations and its own states. Optional per-state parameters (used when it feeds a functional node) and optional stored results when a FunctionalNode is evaluated into a DiscreteNode. Entries may be precise (Real) or imprecise/credal (Interval). Build by name and fill with node[parent1 => sₚ1, ..., name => sₙ] = p.
Examples
W = DiscreteNode(:W) # root node
W[:W => :sunny] = 0.5
W[:W => :cloudy] = 0.5
# a node can carry per-state `parameters` (consumed when it feeds a functional node);
# each of its own states maps to a vector of `Parameter`s and is set at construction:
S = DiscreteNode(:S, [:W], [:on => [Parameter(0.5, :S)], :off => [Parameter(0.0, :S)]])
S[:W => :sunny, :S => :on] = 0.9
S[:W => :sunny, :S => :off] = 0.1
S[:W => :cloudy, :S => :on] = 0.2
S[:W => :cloudy, :S => :off] = 0.8
# credal (imprecise) entries use an Interval instead of a Real:
S[:W => :sunny, :S => :on] = Interval(0.8, 0.95)EnhancedBayesianNetworks.ContinuousNode Type
ContinuousNode(name, parents=Symbol[], discretization=ExactDiscretization(), results=nothing)
ContinuousNode(name, dist::ContinuousProbability)
ContinuousNode(name, dist::ContinuousProbability, discretization::ExactDiscretization)A continuous-valued node. Its table (cpt) maps each parent-state combination to a continuous probability, either precise (a UnivariateDistribution) or imprecise/credal (an Interval or a ProbabilityBox). The discretization field controls how the node is turned into discrete bins during inference: a root node carries an ExactDiscretization (explicit interval edges), a child node an ApproximatedDiscretization (chosen automatically when parents are given).
Build a root either with the ContinuousNode(name, dist) shortcut or by assigning node[] = dist; build a child by naming its parents and filling one distribution per parent state with node[parent => state] = dist.
Examples
# root node from a single distribution:
T = ContinuousNode(:T, Normal()) # root node
# root node with explicit discretization edges (used later during inference):
Td = ContinuousNode(:T, Normal(), ExactDiscretization([-2.0, 0.0, 2.0]))
# child node: one distribution per parent state (discretization is set to Approximated automatically):
C = ContinuousNode(:C, [:W])
C[:W => :sunny] = Normal()
C[:W => :cloudy] = Normal(2, 1)
# imprecise (credal) entries use an Interval instead of a distribution:
C[:W => :sunny] = Interval(0.1, 0.5)EnhancedBayesianNetworks.ContinuousFunctionalNode Type
ContinuousFunctionalNode(name, models, simulation, discretization=ApproximatedDiscretization(), nbins=0)
ContinuousFunctionalNode(name, ancestors::Vector{Symbol}, models, discretization=ApproximatedDiscretization(), nbins=0)A continuous node whose value is computed from its ancestors by one or more UncertaintyQuantification models, instead of being stored as a table. When the node is evaluated, simulation (e.g. MonteCarlo) propagates the parents' uncertainty through the models to produce output samples; these samples are then turned back into a continuous distribution by fitting an EmpiricalDistribution with nbins bins. Separately, discretization (an ApproximatedDiscretization) holds the interval edges used to discretize that continuous node into discrete states for downstream discrete inference. The node is evaluated once per combination of its discrete ancestors, so its uncertainty comes only from its direct parents while the discrete ancestors form the scenario grid the simulation is repeated over. A functional node is never a root; its parents are the inputs referenced by the models, or may be listed explicitly in the second form.
Examples
model = Model(df -> df.x .^ 2, :y) # y is computed from parent x
# propagate x's uncertainty through the model with a Monte Carlo simulation:
CF = ContinuousFunctionalNode(:CF, [model], MonteCarlo(1000))
# use 10 bins when refitting the output's EmpiricalDistribution from the samples:
CFb = ContinuousFunctionalNode(:CF, [model], MonteCarlo(1000), 10)EnhancedBayesianNetworks.DiscreteFunctionalNode Type
DiscreteFunctionalNode(name, models, performance, simulation, parameters=[])
DiscreteFunctionalNode(name, ancestors::Vector{Symbol}, models, performance, parameters=[])A discrete node whose two states — :<name>_safe and :<name>_failed — come from a reliability analysis rather than a table. When the node is evaluated, simulation (e.g. MonteCarlo) propagates the parents' uncertainty through the models, and the performance function maps the models' output to a limit state: the node is failed where performance < 0 and safe otherwise. The estimated failure probability is stored on :<name>_failed and its complement on :<name>_safe. The node is evaluated once per combination of its discrete ancestors, so its uncertainty comes only from its direct parents while the discrete ancestors form the scenario grid the simulation is repeated over. Optional per-state parameters behave as in DiscreteNode: they are forwarded to descendants when this node feeds a further functional node. A functional node is never a root; its parents are the inputs referenced by the models, or may be listed explicitly in the second form.
Examples
model = Model(df -> df.x .^ 2, :y)
performance = df -> df.y .- 1.0 # failed when y < 1
DF = DiscreteFunctionalNode(:DF, [model], performance, MonteCarlo(1000))
states(DF) # [:DF_safe, :DF_failed]
# optional per-state parameters, keyed by the two derived states:
DFp = DiscreteFunctionalNode(:DF, [model], performance, MonteCarlo(1000),
[:DF_safe => [Parameter(1.0, :DF)], :DF_failed => [Parameter(0.0, :DF)]])EnhancedBayesianNetworks.ExactDiscretization Type
ExactDiscretization(intervals=Real[])Discretization strategy for a continuous root node, allowing evidence to be observed on it. The node's distribution support is partitioned exactly at the sorted intervals edges, turning the continuous root into discrete bins. The default (empty intervals) leaves the node continuous, i.e. no discretization is applied. The edges must be sorted.
Examples
# discretize a root node's support at the edges -2, 0, 2:
disc = ExactDiscretization([-2.0, 0.0, 2.0])
T = ContinuousNode(:T, Normal(), disc)
ExactDiscretization() # empty: the root stays continuousEnhancedBayesianNetworks.ApproximatedDiscretization Type
ApproximatedDiscretization(intervals=Real[], sigma=0)Discretization strategy for a continuous non-root (child) node, allowing evidence to be observed on it. The sorted intervals edges partition the support into discrete bins, while sigma is the spread of the normal distribution used to approximate the original continuous distribution's tails when a discrete state is mapped back to a continuous range. sigma must be non-negative; a value above 2 is accepted but warns, as it tends to give an unrealistic tail approximation.
Examples
# discretize a child node at edges -1, 0, 1, approximating tails with spread 1.5:
disc = ApproximatedDiscretization([-1.0, 0.0, 1.0], 1.5)
C = ContinuousNode(:C, [:W], disc)Methods
EnhancedBayesianNetworks.states Function
states(node)Return the vector of discrete states a node can take. Defined for discrete-type nodes: a DiscreteNode returns its own states, a DiscreteFunctionalNode returns its two derived states :<name>_safe and :<name>_failed. Continuous nodes have no discrete states, so calling states on them is a MethodError.
Examples
W = DiscreteNode(:W); W[:W => :sunny] = 0.5; W[:W => :cloudy] = 0.5
states(W) # [:sunny, :cloudy]
m = Model(df -> df.x .^ 2, :y)
DF = DiscreteFunctionalNode(:DF, [:x], m, df -> 1 .- df.y)
states(DF) # [:DF_safe, :DF_failed]EnhancedBayesianNetworks.scenarios Function
scenarios(node)List every row of the node's CPT as a vector of parent => state pairs. For a DiscreteNode each scenario also carries the node's own state; for a ContinuousNode only the parent-state combinations are returned (a continuous node has no discrete state of its own). A root node yields a single empty scenario. Not defined for functional nodes.
Examples
S = DiscreteNode(:S, [:W])
S[:W => :sunny, :S => :on] = 0.9; S[:W => :sunny, :S => :off] = 0.1
S[:W => :cloudy, :S => :on] = 0.2; S[:W => :cloudy, :S => :off] = 0.8
scenarios(S) # [[:W=>:sunny,:S=>:on], [:W=>:sunny,:S=>:off], [:W=>:cloudy,:S=>:on], [:W=>:cloudy,:S=>:off]]
C = ContinuousNode(:C, [:W]); C[:W => :sunny] = Normal(); C[:W => :cloudy] = Normal(2, 1)
scenarios(C) # [[:W => :sunny], [:W => :cloudy]]EnhancedBayesianNetworks.parents Function
parents(node)
parents(net, name::Symbol)
parents(net, node)Return the parent names of a node as a Vector{Symbol} (empty for a root). The single-argument form reads the parents off the node's own table (DiscreteNode/ContinuousNode); the network forms look up the parents of name (or node) within net's topology and work for every node type, including functional nodes.
Examples
W = DiscreteNode(:W); W[:W => :sunny] = 0.5; W[:W => :cloudy] = 0.5
S = DiscreteNode(:S, [:W])
parents(W) # Symbol[]
parents(S) # [:W]
bn = BayesianNetwork([W, S]); add_child!(bn, :W, :S); order!(bn)
parents(bn, :S) # [:W]EnhancedBayesianNetworks.isroot Function
isroot(node)Return true if the node has no parents (a root of the network). A FunctionalNode is never a root (false always), since it is defined by models over its ancestors.
Examples
W = DiscreteNode(:W); W[:W => :sunny] = 0.5; W[:W => :cloudy] = 0.5
isroot(W) # true
S = DiscreteNode(:S, [:W]); isroot(S) # false
T = ContinuousNode(:T, Normal()); isroot(T) # trueEnhancedBayesianNetworks.isprecise Function
isprecise(node)Return true if every entry in the node's CPT is precise, false if any is imprecise/credal. For a DiscreteNode precise means every probability is a Real (imprecise entries are Intervals); for a ContinuousNode precise means every entry is a UnivariateDistribution (imprecise entries are Interval/ProbabilityBox).
Examples
S = DiscreteNode(:S, [:W])
S[:W => :sunny, :S => :on] = 0.9; S[:W => :sunny, :S => :off] = 0.1
S[:W => :cloudy, :S => :on] = 0.2; S[:W => :cloudy, :S => :off] = 0.8
isprecise(S) # true
S[:W => :sunny, :S => :on] = Interval(0.8, 0.95) # a credal entry
isprecise(S) # false
T = ContinuousNode(:T, Normal()); isprecise(T) # trueEnhancedBayesianNetworks.sample Function
sample(node::DiscreteNode, evidence::Evidence)
sample(bn::BayesianNetwork, n::Int=1)Draw discrete samples. Given a DiscreteNode and an Evidence fixing its parents (and, optionally, the node itself), return one sampled state of the node; sampling a node whose entries are imprecise raises an error. Given a BayesianNetwork, perform ancestral sampling of n joint draws (ordering the network first) and return them as a DataFrame with one column per node.
Examples
W = DiscreteNode(:W); W[:W => :sunny] = 0.5; W[:W => :cloudy] = 0.5
S = DiscreteNode(:S, [:W])
S[:W => :sunny, :S => :on] = 0.9; S[:W => :sunny, :S => :off] = 0.1
S[:W => :cloudy, :S => :on] = 0.2; S[:W => :cloudy, :S => :off] = 0.8
sample(S, Evidence(:W => :sunny)) # e.g. :on
bn = BayesianNetwork([W, S]); add_child!(bn, :W, :S); order!(bn)
sample(bn, 3) # 3×2 DataFrame with columns :W, :S