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