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