aGrUM
0.20.3
a C++ library for (probabilistic) graphical models
ShaferShenoyInference.h
Go to the documentation of this file.
1
/**
2
*
3
* Copyright (c) 2005-2021 by Pierre-Henri WUILLEMIN(@LIP6) & Christophe GONZALES(@AMU)
4
* info_at_agrum_dot_org
5
*
6
* This library is free software: you can redistribute it and/or modify
7
* it under the terms of the GNU Lesser General Public License as published by
8
* the Free Software Foundation, either version 3 of the License, or
9
* (at your option) any later version.
10
*
11
* This library is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU Lesser General Public License for more details.
15
*
16
* You should have received a copy of the GNU Lesser General Public License
17
* along with this library. If not, see <http://www.gnu.org/licenses/>.
18
*
19
*/
20
21
22
/**
23
* @file
24
* @brief Implementation of Shafer-Shenoy's algorithm for inference
25
* in Bayesian networks.
26
*/
27
#
ifndef
GUM_SHAFER_SHENOY_INFERENCE_H
28
#
define
GUM_SHAFER_SHENOY_INFERENCE_H
29
30
#
include
<
utility
>
31
32
#
include
<
agrum
/
tools
/
core
/
math
/
math_utils
.
h
>
33
#
include
<
agrum
/
BN
/
algorithms
/
barrenNodesFinder
.
h
>
34
#
include
<
agrum
/
BN
/
inference
/
tools
/
evidenceInference
.
h
>
35
#
include
<
agrum
/
BN
/
inference
/
tools
/
jointTargetedInference
.
h
>
36
#
include
<
agrum
/
agrum
.
h
>
37
#
include
<
agrum
/
tools
/
graphs
/
algorithms
/
triangulations
/
defaultTriangulation
.
h
>
38
39
namespace
gum
{
40
41
42
// the function used to combine two tables
43
template
<
typename
GUM_SCALAR
>
44
INLINE
static
Potential
<
GUM_SCALAR
>*
SSNewmultiPotential
(
const
Potential
<
GUM_SCALAR
>&
t1
,
45
const
Potential
<
GUM_SCALAR
>&
t2
) {
46
return
new
Potential
<
GUM_SCALAR
>(
t1
*
t2
);
47
}
48
49
// the function used to combine two tables
50
template
<
typename
GUM_SCALAR
>
51
INLINE
static
Potential
<
GUM_SCALAR
>*
52
SSNewprojPotential
(
const
Potential
<
GUM_SCALAR
>&
t1
,
53
const
Set
<
const
DiscreteVariable
* >&
del_vars
) {
54
return
new
Potential
<
GUM_SCALAR
>(
t1
.
margSumOut
(
del_vars
));
55
}
56
57
58
/**
59
* @class ShaferShenoyInference ShaferShenoyInference.h
60
* <agrum/BN/inference/ShaferShenoyInference.h>
61
* @brief Implementation of Shafer-Shenoy's propagation algorithm
62
* for inference in Bayesian networks
63
* @ingroup bn_inference
64
*/
65
template
<
typename
GUM_SCALAR
>
66
class
ShaferShenoyInference
:
67
public
JointTargetedInference
<
GUM_SCALAR
>,
68
public
EvidenceInference
<
GUM_SCALAR
> {
69
public
:
70
// ############################################################################
71
/// @name Constructors / Destructors
72
// ############################################################################
73
/// @{
74
75
/// default constructor
76
explicit
ShaferShenoyInference
(
const
IBayesNet
<
GUM_SCALAR
>*
BN
,
77
FindBarrenNodesType
barren_type
78
=
FindBarrenNodesType
::
FIND_BARREN_NODES
,
79
bool
use_binary_join_tree
=
true
);
80
81
/// destructor
82
~
ShaferShenoyInference
()
final
;
83
84
/// @}
85
86
87
// ############################################################################
88
/// @name Accessors / Modifiers
89
// ############################################################################
90
/// @{
91
92
/// use a new triangulation algorithm
93
void
setTriangulation
(
const
Triangulation
&
new_triangulation
);
94
95
/// sets how we determine barren nodes
96
/** Barren nodes are unnecessary for probability inference, so they can
97
* be safely discarded in this case (type = FIND_BARREN_NODES). This
98
* speeds-up inference. However, there are some cases in which we do not
99
* want to remove barren nodes, typically when we want to answer queries
100
* such as Most Probable Explanations (MPE). */
101
void
setFindBarrenNodesType
(
FindBarrenNodesType
type
);
102
103
/// returns the current join tree used
104
/** Lazy Propagation does not use a junction tree but a binary join tree
105
* because this may enable faster inferences. So do not be surprised to
106
* see that somes cliques are contained into others in this tree. */
107
const
JoinTree
*
joinTree
();
108
109
/// returns the current junction tree
110
/** Lazy Propagation does not use a junction tree but a binary join tree
111
* because this may enable faster inferences. This method return the junction
112
* tree, before optimizations
113
**/
114
const
JunctionTree
*
junctionTree
();
115
116
/// returns the probability of evidence
117
GUM_SCALAR
evidenceProbability
();
118
119
/// @}
120
121
122
protected
:
123
/// fired when the stage is changed
124
void
onStateChanged_
()
final
{};
125
126
/// fired after a new evidence is inserted
127
void
onEvidenceAdded_
(
const
NodeId
id
,
bool
isHardEvidence
)
final
;
128
129
/// fired before an evidence is removed
130
void
onEvidenceErased_
(
const
NodeId
id
,
bool
isHardEvidence
)
final
;
131
132
/// fired before all the evidence are erased
133
void
onAllEvidenceErased_
(
bool
contains_hard_evidence
)
final
;
134
135
/** @brief fired after an evidence is changed, in particular when its status
136
* (soft/hard) changes
137
*
138
* @param nodeId the node of the changed evidence
139
* @param hasChangedSoftHard true if the evidence has changed from Soft to
140
* Hard or from Hard to Soft
141
*/
142
void
onEvidenceChanged_
(
const
NodeId
id
,
bool
hasChangedSoftHard
)
final
;
143
144
/// fired after a new single target is inserted
145
/** @param id The target variable's id. */
146
void
onMarginalTargetAdded_
(
const
NodeId
id
)
final
;
147
148
/// fired before a single target is removed
149
/** @param id The target variable's id. */
150
void
onMarginalTargetErased_
(
const
NodeId
id
)
final
;
151
152
/// fired after a new Bayes net has been assigned to the engine
153
virtual
void
onModelChanged_
(
const
GraphicalModel
*
bn
)
final
;
154
155
/// fired after a new joint target is inserted
156
/** @param set The set of target variable's ids. */
157
void
onJointTargetAdded_
(
const
NodeSet
&
set
)
final
;
158
159
/// fired before a joint target is removed
160
/** @param set The set of target variable's ids. */
161
void
onJointTargetErased_
(
const
NodeSet
&
set
)
final
;
162
163
/// fired after all the nodes of the BN are added as single targets
164
void
onAllMarginalTargetsAdded_
()
final
;
165
166
/// fired before a all the single targets are removed
167
void
onAllMarginalTargetsErased_
()
final
;
168
169
/// fired before a all the joint targets are removed
170
void
onAllJointTargetsErased_
()
final
;
171
172
/// fired before a all single and joint_targets are removed
173
void
onAllTargetsErased_
()
final
;
174
175
/// prepares inference when the latter is in OutdatedStructure state
176
/** Note that the values of evidence are not necessarily
177
* known and can be changed between updateOutdatedStructure_ and
178
* makeInference_. */
179
void
updateOutdatedStructure_
()
final
;
180
181
/// prepares inference when the latter is in OutdatedPotentials state
182
/** Note that the values of evidence are not necessarily
183
* known and can be changed between updateOutdatedStructure_ and
184
* makeInference_. */
185
void
updateOutdatedPotentials_
()
final
;
186
187
/// called when the inference has to be performed effectively
188
/** Once the inference is done, fillPosterior_ can be called. */
189
void
makeInference_
()
final
;
190
191
192
/// returns the posterior of a given variable
193
/** @param id The variable's id. */
194
const
Potential
<
GUM_SCALAR
>&
posterior_
(
NodeId
id
)
final
;
195
196
/// returns the posterior of a declared target set
197
/** @param set The set of ids of the variables whose joint posterior is
198
* looked for. */
199
const
Potential
<
GUM_SCALAR
>&
jointPosterior_
(
const
NodeSet
&
set
)
final
;
200
201
/** @brief asks derived classes for the joint posterior of a set of
202
* variables not declared as a joint target
203
*
204
* @param wanted_target The set of ids of the variables whose joint
205
* posterior is looked for.
206
* @param declared_target the joint target declared by the user that
207
* contains set */
208
const
Potential
<
GUM_SCALAR
>&
jointPosterior_
(
const
NodeSet
&
wanted_target
,
209
const
NodeSet
&
declared_target
)
final
;
210
211
/// returns a fresh potential equal to P(argument,evidence)
212
Potential
<
GUM_SCALAR
>*
unnormalizedJointPosterior_
(
NodeId
id
)
final
;
213
214
/// returns a fresh potential equal to P(argument,evidence)
215
Potential
<
GUM_SCALAR
>*
unnormalizedJointPosterior_
(
const
NodeSet
&
set
)
final
;
216
217
218
private
:
219
typedef
Set
<
const
Potential
<
GUM_SCALAR
>* >
_PotentialSet_
;
220
typedef
SetIteratorSafe
<
const
Potential
<
GUM_SCALAR
>* >
_PotentialSetIterator_
;
221
222
223
/// the type of barren nodes computation we wish
224
FindBarrenNodesType
_barren_nodes_type_
;
225
226
/// the operator for performing the projections
227
Potential
<
GUM_SCALAR
>* (*
_projection_op_
)(
const
Potential
<
GUM_SCALAR
>&,
228
const
Set
<
const
DiscreteVariable
* >&){
229
SSNewprojPotential
};
230
231
/// the operator for performing the combinations
232
Potential
<
GUM_SCALAR
>* (*
_combination_op_
)(
const
Potential
<
GUM_SCALAR
>&,
233
const
Potential
<
GUM_SCALAR
>&){
234
SSNewmultiPotential
};
235
236
/// the triangulation class creating the junction tree used for inference
237
Triangulation
*
_triangulation_
;
238
239
/** @brief indicates whether we should transform junction trees into
240
* binary join trees */
241
bool
_use_binary_join_tree_
{
true
};
242
243
/// the undigraph extracted from the BN and used to construct the join tree
244
/** If all nodes are targets, this graph corresponds to the moral graph
245
* of the BN. Otherwise, it may be a subgraph of this moral graph. For
246
* instance if the BN is A->B->C and only B is a target, _graph_ will be
247
* equal to A-B if we exploit barren nodes (C is a barren node and,
248
* therefore, can be removed for inference). */
249
UndiGraph
_graph_
;
250
251
/// the join (or junction) tree used to answer the last inference query
252
JoinTree
*
_JT_
{
nullptr
};
253
254
/// the junction tree to answer the last inference query
255
JunctionTree
*
_junctionTree_
{
nullptr
};
256
257
/// indicates whether a new join tree is needed for the next inference
258
/** when modifying the set of hard evidence, we can determine that
259
* the current JT is no more appropriate for inference. This variable
260
* enables us to keep track of this. */
261
bool
_is_new_jt_needed_
{
true
};
262
263
/// a clique node used as a root in each connected component of _JT_
264
/** For usual probabilistic inference, roots is useless. This is useful
265
* when computing the probability of evidence. In this case, we need to
266
* compute this probability in every connected component and multiply
267
* them to get the overall probability of evidence.
268
* @warning _roots_ should be computed only when evidenceProbability
269
* is called. */
270
NodeSet
_roots_
;
271
272
/// for each node of _graph_ (~ in the Bayes net), associate an ID in the JT
273
HashTable
<
NodeId
,
NodeId
>
_node_to_clique_
;
274
275
/// for each set target, assign a clique in the JT that contains it
276
HashTable
<
NodeSet
,
NodeId
>
_joint_target_to_clique_
;
277
278
/// the list of all potentials stored in the cliques
279
/** This structure contains a list for each clique in the join tree. If
280
* a clique did not received any potential, then its list is empty but
281
* the entry for the clique does exist. Note that clique potentials
282
* contain also soft evidence and the CPTs that were projected to
283
* remove their variables that received hard evidence. The product of all
284
* these potentials is precisely the potential stored into
285
* _clique_ss_potential_ */
286
NodeProperty
<
_PotentialSet_
>
_clique_potentials_
;
287
288
/// the potentials stored into the cliques by Shafer-Shenoy
289
/** For a given clique, there is an entry in _clique_ss_potential_ if and
290
* only if the clique received some potential(s). In this case, the
291
* potential stored is the combination of all the corresponding list of
292
* potentials in _clique_potentials_. */
293
NodeProperty
<
const
Potential
<
GUM_SCALAR
>* >
_clique_ss_potential_
;
294
295
/// the list of all potentials stored in the separators after inferences
296
/** This structure contains all the arcs of the join tree (edges in both
297
* directions) whether the arc received any potential or not. */
298
ArcProperty
<
_PotentialSet_
>
_separator_potentials_
;
299
300
/// the set of potentials created for the last inference messages
301
/** This structure contains only the arcs on which potentials have
302
* been created.
303
* @warning Note that the CPTs that were projected due to hard
304
* evidence do not belong to this structure, they are kept in
305
* _hard_ev_projected_CPTs_. */
306
ArcProperty
<
_PotentialSet_
>
_created_potentials_
;
307
308
/// the set of single posteriors computed during the last inference
309
/** the posteriors are owned by ShaferShenoyInference. */
310
NodeProperty
<
const
Potential
<
GUM_SCALAR
>* >
_target_posteriors_
;
311
312
/// the set of set target posteriors computed during the last inference
313
/** the posteriors are owned by ShaferShenoyInference. */
314
HashTable
<
NodeSet
,
const
Potential
<
GUM_SCALAR
>* >
_joint_target_posteriors_
;
315
316
/** @brief the constants resulting from the projections of CPTs defined
317
* over only hard evidence nodes
318
* @TODO remove this constant and insert the notion of a constant into
319
* potentials/multidim arrays */
320
NodeProperty
<
GUM_SCALAR
>
_constants_
;
321
322
/// indicates whether a message (from one clique to another) has been
323
/// computed
324
/** Here, all the messages, computed or not, are put into the property, only
325
* the Boolean makes the difference between messages computed and those that
326
* were not computed */
327
ArcProperty
<
bool
>
_messages_computed_
;
328
329
/// the soft evidence stored in the cliques per their assigned node in the BN
330
/** This variable is useful for method updateOutdatedPotentials_: it
331
* enables to know which soft evidence should be removed/added into the
332
* cliques of the join tree.
333
* @warning These potentials are not owned by ShaferShenoyInference,
334
* they are only referenced by it. Only the cliques that contain evidence
335
* are filled in this structure. */
336
NodeProperty
<
const
Potential
<
GUM_SCALAR
>* >
_node_to_soft_evidence_
;
337
338
/// the CPTs that were projected due to hard evidence nodes
339
/** For each node whose CPT is defined over some nodes that contain some
340
* hard evidence, assigns a new projected CPT that does not contain
341
* these nodes anymore.
342
* @warning These potentials are owned by LayPropagation. */
343
NodeProperty
<
const
Potential
<
GUM_SCALAR
>* >
_hard_ev_projected_CPTs_
;
344
345
/// the hard evidence nodes which were projected in CPTs
346
NodeSet
_hard_ev_nodes_
;
347
348
/// the possible types of evidence changes
349
enum
EvidenceChangeType
350
{
351
EVIDENCE_ADDED
,
352
EVIDENCE_ERASED
,
353
EVIDENCE_MODIFIED
354
};
355
356
/** @brief indicates which nodes of the BN have evidence that changed
357
* since the last inference */
358
NodeProperty
<
EvidenceChangeType
>
_evidence_changes_
;
359
360
/// for comparisons with 1 - epsilon
361
const
GUM_SCALAR
_one_minus_epsilon_
{
GUM_SCALAR
(1.0 - 1e-6)};
362
363
364
/// check whether a new join tree is really needed for the next inference
365
bool
_isNewJTNeeded_
()
const
;
366
367
/// create a new junction tree as well as its related data structures
368
void
_createNewJT_
();
369
/// sets the operator for performing the projections
370
void
_setProjectionFunction_
(
Potential
<
GUM_SCALAR
>* (
371
*
proj
)(
const
Potential
<
GUM_SCALAR
>&,
const
Set
<
const
DiscreteVariable
* >&));
372
373
/// sets the operator for performing the combinations
374
void
_setCombinationFunction_
(
Potential
<
GUM_SCALAR
>* (*
comb
)(
const
Potential
<
GUM_SCALAR
>&,
375
const
Potential
<
GUM_SCALAR
>&));
376
377
/// invalidate all the messages sent from a given clique
378
void
_diffuseMessageInvalidations_
(
NodeId
from
,
NodeId
to
,
NodeSet
&
cliques_invalidated
);
379
380
/// invalidate all messages, posteriors and created potentials
381
void
_invalidateAllMessages_
();
382
383
/// compute a root for each connected component of _JT_
384
void
_computeJoinTreeRoots_
();
385
386
// remove barren variables and return the newly created projected potentials
387
_PotentialSet_
_removeBarrenVariables_
(
_PotentialSet_
&
pot_list
,
388
Set
<
const
DiscreteVariable
* >&
del_vars
);
389
390
/** @brief removes variables del_vars from a list of potentials and
391
* returns the resulting list */
392
_PotentialSet_
_marginalizeOut_
(
_PotentialSet_
pot_list
,
393
Set
<
const
DiscreteVariable
* >&
del_vars
,
394
Set
<
const
DiscreteVariable
* >&
kept_vars
);
395
396
/// creates the message sent by clique from_id to clique to_id
397
void
_produceMessage_
(
NodeId
from_id
,
NodeId
to_id
);
398
399
/// actually perform the collect phase
400
void
_collectMessage_
(
NodeId
id
,
NodeId
from
);
401
402
/// avoid copy constructors
403
ShaferShenoyInference
(
const
ShaferShenoyInference
<
GUM_SCALAR
>&);
404
405
/// avoid copy operators
406
ShaferShenoyInference
<
GUM_SCALAR
>&
operator
=(
const
ShaferShenoyInference
<
GUM_SCALAR
>&);
407
};
408
409
410
#
ifndef
GUM_NO_EXTERN_TEMPLATE_CLASS
411
extern
template
class
ShaferShenoyInference<
double
>;
412
#
endif
413
414
415
}
/* namespace gum */
416
417
#
include
<
agrum
/
BN
/
inference
/
ShaferShenoyInference_tpl
.
h
>
418
419
#
endif
/* SHAFER_SHENOY_INFERENCE_H */
gum::Set::emplace
INLINE void emplace(Args &&... args)
Definition:
set_tpl.h:643