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