aGrUM
0.20.2
a C++ library for (probabilistic) graphical models
ShaferShenoyLIMIDInference_tpl.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 Implementations of the classes defined
25
* in InfluenceDiagram/inference/ShaferShenoyLIMIDInference.h.
26
*/
27
28
#
ifndef
DOXYGEN_SHOULD_SKIP_THIS
29
30
// to ease parsing by IDE
31
#
include
<
limits
>
32
33
#
include
<
agrum
/
ID
/
inference
/
ShaferShenoyLIMIDInference
.
h
>
34
#
include
<
agrum
/
ID
/
inference
/
tools
/
decisionPotential
.
h
>
35
#
include
<
agrum
/
tools
/
multidim
/
potential
.
h
>
36
37
//# define GUM_SSLI_TRACE_ON
38
//# define GUM_SSLI_POTENTIAL_TRACE_ON
39
40
namespace
gum
{
41
42
template
<
typename
GUM_SCALAR >
43
ShaferShenoyLIMIDInference< GUM_SCALAR >::ShaferShenoyLIMIDInference(
44
const
InfluenceDiagram< GUM_SCALAR >* infDiag) :
45
InfluenceDiagramInference< GUM_SCALAR >(infDiag) {
46
GUM_CONSTRUCTOR(ShaferShenoyLIMIDInference);
47
createReduced_();
48
}
49
50
template
<
typename
GUM_SCALAR
>
51
ShaferShenoyLIMIDInference
<
GUM_SCALAR
>::~
ShaferShenoyLIMIDInference
() {
52
GUM_DESTRUCTOR
(
ShaferShenoyLIMIDInference
);
53
}
54
template
<
typename
GUM_SCALAR
>
55
void
ShaferShenoyLIMIDInference
<
GUM_SCALAR
>::
clear
() {
56
GraphicalModelInference
<
GUM_SCALAR
>::
clear
();
57
noForgettingOrder_
.
clear
();
58
reduced_
.
clear
();
59
reducedJunctionTree_
.
clear
();
60
solvabilityOrder_
.
clear
();
61
posteriors_
.
clear
();
62
unconditionalDecisions_
.
clear
();
63
strategies_
.
clear
();
64
reversePartialOrder_
.
clear
();
65
}
66
template
<
typename
GUM_SCALAR
>
67
void
ShaferShenoyLIMIDInference
<
GUM_SCALAR
>::
onStateChanged_
() {}
68
template
<
typename
GUM_SCALAR
>
69
void
ShaferShenoyLIMIDInference
<
GUM_SCALAR
>::
onEvidenceAdded_
(
70
const
NodeId
id
,
71
bool
isHardEvidence
) {
72
const
InfluenceDiagram
<
GUM_SCALAR
>&
infdiag
=
this
->
influenceDiagram
();
73
if
(
infdiag
.
isUtilityNode
(
id
)) {
74
GUM_ERROR
(
InvalidNode
,
"No evidence on a utility node."
);
75
}
76
if
(
infdiag
.
isDecisionNode
(
id
)) {
77
if
(!
isHardEvidence
)
78
GUM_ERROR
(
InvalidNode
,
"No soft evidence on a decision node."
);
79
}
80
}
81
template
<
typename
GUM_SCALAR
>
82
void
ShaferShenoyLIMIDInference
<
GUM_SCALAR
>::
onEvidenceErased_
(
83
const
NodeId
id
,
84
bool
isHardEvidence
) {}
85
template
<
typename
GUM_SCALAR
>
86
void
ShaferShenoyLIMIDInference
<
GUM_SCALAR
>::
onAllEvidenceErased_
(
87
bool
contains_hard_evidence
) {}
88
template
<
typename
GUM_SCALAR
>
89
void
ShaferShenoyLIMIDInference
<
GUM_SCALAR
>::
onEvidenceChanged_
(
90
const
NodeId
id
,
91
bool
hasChangedSoftHard
) {}
92
template
<
typename
GUM_SCALAR
>
93
void
ShaferShenoyLIMIDInference
<
GUM_SCALAR
>::
onModelChanged_
(
94
const
GraphicalModel
*
model
) {
95
createReduced_
();
96
}
97
98
template
<
typename
GUM_SCALAR
>
99
void
ShaferShenoyLIMIDInference
<
GUM_SCALAR
>::
updateOutdatedStructure_
() {
100
createReduced_
();
101
}
102
template
<
typename
GUM_SCALAR
>
103
void
ShaferShenoyLIMIDInference
<
GUM_SCALAR
>::
updateOutdatedPotentials_
() {}
104
105
template
<
typename
GUM_SCALAR
>
106
void
ShaferShenoyLIMIDInference
<
GUM_SCALAR
>::
makeInference_
() {
107
if
(!
isSolvable
()) {
108
GUM_ERROR
(
FatalError
,
"This LIMID/Influence Diagram is not solvable."
)
109
}
110
111
PhiNodeProperty
phi
;
112
PsiArcProperty
psi
;
113
114
initializingInference_
(
phi
,
psi
);
115
#
ifdef
GUM_SSLI_TRACE_ON
116
GUM_TRACE_VAR
(
reducedJunctionTree_
.
toDot
())
117
#
endif
118
// message passing (using reverse order of solvabilityOrder)
119
// first collect of phis into root
120
const
auto
firstRootIndice
= 0;
121
collectingMessage_
(
phi
,
122
psi
,
123
node_to_clique_
[
solvabilityOrder_
[
firstRootIndice
]]);
124
deciding_
(
phi
,
psi
,
solvabilityOrder_
[
firstRootIndice
]);
125
126
for
(
Idx
nextRootIndice
= 1;
nextRootIndice
<
solvabilityOrder_
.
size
();
127
nextRootIndice
++) {
128
if
(
node_to_clique_
[
solvabilityOrder_
[
nextRootIndice
- 1]]
129
!=
node_to_clique_
[
solvabilityOrder_
[
nextRootIndice
]]) {
130
collectingToFollowingRoot_
(
131
phi
,
132
psi
,
133
node_to_clique_
[
solvabilityOrder_
[
nextRootIndice
- 1]],
134
node_to_clique_
[
solvabilityOrder_
[
nextRootIndice
]]);
135
}
136
deciding_
(
phi
,
psi
,
solvabilityOrder_
[
nextRootIndice
]);
137
}
138
139
// last distribution
140
distributingMessage_
(
phi
,
psi
,
node_to_clique_
[
solvabilityOrder_
[0]]);
141
computingPosteriors_
(
phi
,
psi
);
142
}
143
144
template
<
typename
GUM_SCALAR
>
145
void
ShaferShenoyLIMIDInference
<
GUM_SCALAR
>::
initializingInference_
(
146
PhiNodeProperty
&
phi
,
147
PsiArcProperty
&
psi
) {
148
const
auto
&
jt
= *
junctionTree
();
149
const
auto
&
infdiag
=
this
->
influenceDiagram
();
150
// init JT potentials and separators
151
152
for
(
const
auto
node
:
jt
.
nodes
()) {
153
phi
.
insert
(
node
,
DecisionPotential
<
GUM_SCALAR
>());
154
for
(
const
auto
nei
:
jt
.
neighbours
(
node
)) {
155
psi
.
insert
(
Arc
(
node
,
nei
),
DecisionPotential
<
GUM_SCALAR
>());
156
if
(
node
<
nei
) {
// to do it only once by edge
157
// we create the set of vars in node and nei (cached in varsSeparators_)
158
for
(
const
auto
n
:
jt
.
clique
(
node
) *
jt
.
clique
(
nei
))
159
varsSeparator_
.
getWithDefault
(
Edge
(
node
,
nei
),
SetOfVars
())
160
.
insert
(&(
infdiag
.
variable
(
n
)));
161
}
162
}
163
}
164
for
(
const
auto
node
:
infdiag
.
nodes
()) {
165
const
auto
clik
=
node_to_clique_
[
node
];
166
if
(
this
->
hasEvidence
(
node
)) {
167
auto
q
= *(
this
->
evidence
()[
node
]);
168
phi
[
clik
].
insertProba
(
q
.
normalize
());
169
}
170
171
if
(
infdiag
.
isDecisionNode
(
node
)) {
172
if
(!
this
->
hasEvidence
(
node
)) {
173
auto
p
= (
Potential
<
GUM_SCALAR
>() <<
infdiag
.
variable
(
node
))
174
.
fillWith
(1)
175
.
normalize
();
176
phi
[
clik
].
insertProba
(
p
);
// WITHOUT NORMALIZATION !!!
177
}
178
}
else
if
(
infdiag
.
isChanceNode
(
node
))
179
phi
[
clik
].
insertProba
(
infdiag
.
cpt
(
node
));
180
else
if
(
infdiag
.
isUtilityNode
(
node
))
181
phi
[
clik
].
insertUtility
(
infdiag
.
utility
(
node
));
182
else
183
GUM_ERROR
(
FatalError
,
"Node "
<<
node
<<
" has no type."
);
184
}
185
}
186
187
template
<
typename
GUM_SCALAR
>
188
void
ShaferShenoyLIMIDInference
<
GUM_SCALAR
>::
creatingJunctionTree__
() {
189
const
auto
&
infdiag
=
this
->
influenceDiagram
();
190
auto
moral
=
reduced_
.
moralGraph
();
191
192
// once the moral graph is completed, we remove the utility nodes before
193
// triangulation
194
NodeProperty
<
Size
>
modalities
;
195
for
(
const
auto
node
:
infdiag
.
nodes
())
196
if
(
infdiag
.
isUtilityNode
(
node
)) {
197
moral
.
eraseNode
(
node
);
198
}
else
{
199
modalities
.
insert
(
node
,
infdiag
.
variable
(
node
).
domainSize
());
200
}
201
DefaultTriangulation
triangulation
(&
moral
, &
modalities
);
202
reducedJunctionTree_
=
triangulation
.
junctionTree
();
203
findingCliqueForEachNode__
(
triangulation
);
204
}
205
206
template
<
typename
GUM_SCALAR
>
207
void
ShaferShenoyLIMIDInference
<
GUM_SCALAR
>::
findingCliqueForEachNode__
(
208
DefaultTriangulation
&
triangulation
) {
209
// indicate, for each node of the moral graph a clique in JT__ that can
210
// contain its conditional probability table
211
const
auto
&
infdiag
=
this
->
influenceDiagram
();
212
NodeId
first_eliminated_node
;
213
Idx
elim_number
;
214
node_to_clique_
.
clear
();
215
const
std
::
vector
<
NodeId
>&
JT_elim_order
=
triangulation
.
eliminationOrder
();
216
NodeProperty
<
Idx
>
elim_order
(
Size
(
JT_elim_order
.
size
()));
217
for
(
Idx
i
=
Idx
(0),
size
=
JT_elim_order
.
size
();
i
<
size
; ++
i
)
218
elim_order
.
insert
(
JT_elim_order
[
i
], (
int
)
i
);
219
for
(
const
auto
node
:
reduced_
.
nodes
()) {
220
if
(
infdiag
.
isUtilityNode
(
node
)) {
221
// utility nodes are not in the junction tree but we want to associate a
222
// clique as well
223
first_eliminated_node
=
node
;
224
elim_number
225
=
std
::
numeric_limits
<
NodeId
>::
max
();
// an impossible elim_number;
226
}
else
{
227
// get the variables in the potential of node (and its parents)
228
first_eliminated_node
=
node
;
229
elim_number
=
elim_order
[
first_eliminated_node
];
230
}
231
232
for
(
const
auto
parent
:
reduced_
.
parents
(
node
)) {
233
if
(
elim_order
[
parent
] <
elim_number
) {
234
elim_number
=
elim_order
[
parent
];
235
first_eliminated_node
=
parent
;
236
}
237
}
238
239
// first_eliminated_node contains the first var (node or one of its
240
// parents) eliminated => the clique created during its elimination
241
// contains node and all of its parents => it can contain the potential
242
// assigned to the node in the BN
243
node_to_clique_
.
insert
(
244
node
,
245
triangulation
.
createdJunctionTreeClique
(
first_eliminated_node
));
246
}
247
}
248
249
template
<
typename
GUM_SCALAR
>
250
std
::
pair
<
GUM_SCALAR
,
GUM_SCALAR
>
251
ShaferShenoyLIMIDInference
<
GUM_SCALAR
>::
MEU
() {
252
if
(!
this
->
isInferenceDone
())
253
GUM_ERROR
(
OperationNotAllowed
,
"Call MakeInference first"
)
254
255
const
InfluenceDiagram
<
GUM_SCALAR
>&
infdiag
=
this
->
influenceDiagram
();
256
257
GUM_SCALAR
resmean
= 0;
258
GUM_SCALAR
resvar
= 0;
259
for
(
auto
node
:
infdiag
.
nodes
()) {
260
if
(
infdiag
.
isUtilityNode
(
node
)) {
261
#
ifdef
GUM_SSLI_TRACE_ON
262
GUM_TRACE_VAR
(
posteriors_
[
node
])
263
#
endif
264
auto
p
=
meanVar
(
node
);
265
resmean
+=
p
.
first
;
266
resvar
+=
p
.
second
;
267
}
268
}
269
return
std
::
pair
<
GUM_SCALAR
,
GUM_SCALAR
>(
resmean
,
resvar
);
270
}
271
272
template
<
typename
GUM_SCALAR
>
273
gum
::
Potential
<
GUM_SCALAR
>
274
ShaferShenoyLIMIDInference
<
GUM_SCALAR
>::
optimalDecision
(
NodeId
decisionId
) {
275
if
(!
this
->
isInferenceDone
())
276
GUM_ERROR
(
OperationNotAllowed
,
"Call MakeInference first"
)
277
278
const
InfluenceDiagram
<
GUM_SCALAR
>&
infdiag
=
this
->
influenceDiagram
();
279
if
(!
infdiag
.
isDecisionNode
(
decisionId
))
280
GUM_ERROR
(
InvalidNode
,
281
infdiag
.
variable
(
decisionId
).
name
()
282
<<
"("
<<
decisionId
<<
") is not a decision node."
)
283
284
return
strategies_
[
decisionId
];
285
}
286
287
288
template
<
typename
GUM_SCALAR
>
289
bool
ShaferShenoyLIMIDInference
<
GUM_SCALAR
>::
isSolvable
()
const
{
290
return
(!
solvabilityOrder_
.
empty
());
291
}
292
293
template
<
typename
GUM_SCALAR
>
294
void
ShaferShenoyLIMIDInference
<
GUM_SCALAR
>::
createReduced_
() {
295
// from LIMIDS of decision Problems, Lauritzen et Nilsson, 1999
296
reduced_
.
clear
();
297
reducedJunctionTree_
.
clear
();
298
solvabilityOrder_
.
clear
();
299
reversePartialOrder_
.
clear
();
300
posteriors_
.
clear
();
301
unconditionalDecisions_
.
clear
();
302
strategies_
.
clear
();
303
const
InfluenceDiagram
<
GUM_SCALAR
>&
infdiag
=
this
->
influenceDiagram
();
304
305
NodeSet
utilities
;
306
307
// build reduced_
308
for
(
auto
node
:
infdiag
.
nodes
()) {
309
reduced_
.
addNodeWithId
(
node
);
310
if
(
infdiag
.
isUtilityNode
(
node
)) {
utilities
.
insert
(
node
); }
311
}
312
313
for
(
const
auto
&
arc
:
infdiag
.
arcs
())
314
reduced_
.
addArc
(
arc
.
tail
(),
arc
.
head
());
315
316
completingNoForgettingAssumption__
();
317
creatingPartialOrder__
(
utilities
);
318
checkingSolvability__
(
utilities
);
319
if
(
isSolvable
()) {
320
reducingLIMID__
();
321
creatingJunctionTree__
();
322
}
323
324
this
->
setState_
(
325
GraphicalModelInference
<
GUM_SCALAR
>::
StateOfInference
::
OutdatedStructure
);
326
}
327
328
template
<
typename
GUM_SCALAR
>
329
void
ShaferShenoyLIMIDInference
<
330
GUM_SCALAR
>::
completingNoForgettingAssumption__
() {
331
// force no forgetting if necessary
332
if
(
hasNoForgettingAssumption
()) {
333
auto
last
= *(
noForgettingOrder_
.
begin
());
334
for
(
auto
node
:
noForgettingOrder_
)
335
if
(
node
==
last
)
// first one
336
continue
;
337
else
{
// we deal with last->node
338
// adding the whole family of last as parents of node
339
if
(!
reduced_
.
existsArc
(
last
,
node
)) {
reduced_
.
addArc
(
last
,
node
); }
340
for
(
auto
par
:
reduced_
.
parents
(
last
)) {
341
if
(!
reduced_
.
existsArc
(
par
,
node
))
reduced_
.
addArc
(
par
,
node
);
342
}
343
last
=
node
;
344
}
345
}
346
}
347
348
template
<
typename
GUM_SCALAR
>
349
void
ShaferShenoyLIMIDInference
<
GUM_SCALAR
>::
checkingSolvability__
(
350
const
NodeSet
&
utilities
) {
351
if
(
hasNoForgettingAssumption
()) {
352
solvabilityOrder_
=
noForgettingOrder_
;
353
std
::
reverse
(
solvabilityOrder_
.
begin
(),
solvabilityOrder_
.
end
());
354
return
;
355
}
356
357
solvabilityOrder_
.
clear
();
358
for
(
const
auto
&
sen
:
reversePartialOrder
()) {
359
NodeSet
tobetested
=
sen
;
360
while
(!
tobetested
.
empty
()) {
361
bool
foundOne
=
false
;
362
for
(
const
auto
&
node
:
tobetested
) {
363
const
auto
us
=
utilities
*
reduced_
.
descendants
(
node
);
364
NodeSet
decs
;
365
for
(
const
auto
dec
:
tobetested
)
366
if
(
dec
!=
node
)
decs
+=
reduced_
.
family
(
dec
);
367
if
(
reduced_
.
dSeparation
(
decs
,
us
,
reduced_
.
family
(
node
))) {
368
solvabilityOrder_
.
push_back
(
node
);
369
foundOne
=
true
;
370
tobetested
.
erase
(
node
);
371
break
;
372
}
373
}
374
if
(!
foundOne
) {
// no solvability
375
solvabilityOrder_
.
clear
();
376
return
;
377
}
378
}
379
}
380
}
381
382
template
<
typename
GUM_SCALAR
>
383
void
ShaferShenoyLIMIDInference
<
GUM_SCALAR
>::
reducingLIMID__
() {
384
for
(
const
auto
&
sen
:
reversePartialOrder_
) {
385
for
(
auto
n
:
sen
) {
386
for
(
auto
p
:
nonRequisiteNodes_
(
n
))
387
reduced_
.
eraseArc
(
Arc
(
p
,
n
));
388
}
389
}
390
}
391
392
template
<
typename
GUM_SCALAR
>
393
void
ShaferShenoyLIMIDInference
<
GUM_SCALAR
>::
creatingPartialOrder__
(
394
const
NodeSet
&
utilities
) {
395
const
InfluenceDiagram
<
GUM_SCALAR
>&
infdiag
=
this
->
influenceDiagram
();
396
NodeProperty
<
Size
>
level
;
397
398
for
(
const
auto
&
node
:
utilities
)
399
level
.
insert
(
node
, 0);
// utility node is level 0
400
401
// creating the partial order
402
Size
max_level
= 0;
403
reversePartialOrder_
.
clear
();
404
reversePartialOrder_
.
resize
(
infdiag
.
size
());
405
NodeSet
currents
;
406
for
(
auto
node
:
infdiag
.
nodes
()) {
407
if
(
infdiag
.
isUtilityNode
(
node
))
continue
;
408
if
(
reduced_
.
children
(
node
).
isSubsetOrEqual
(
utilities
)) {
409
currents
.
clear
();
410
currents
.
insert
(
node
);
411
level
.
insert
(
node
, 0);
412
while
(!
currents
.
empty
()) {
413
NodeId
elt
= *(
currents
.
begin
());
414
currents
.
erase
(
elt
);
415
416
if
(
infdiag
.
isDecisionNode
(
elt
))
417
reversePartialOrder_
[
level
[
elt
]].
insert
(
elt
);
418
419
for
(
auto
parent
:
reduced_
.
parents
(
elt
)) {
420
Size
lev
= 0;
421
Size
newl
;
422
bool
ok_to_add
=
true
;
423
for
(
auto
child
:
reduced_
.
children
(
parent
)) {
424
if
(!
level
.
exists
(
child
)) {
425
ok_to_add
=
false
;
426
break
;
427
}
428
newl
=
level
[
child
];
429
if
(
infdiag
.
isDecisionNode
(
child
))
newl
+= 1;
430
if
(
lev
<
newl
)
lev
=
newl
;
431
}
432
if
(
ok_to_add
) {
433
currents
.
insert
(
parent
);
434
if
(
level
.
exists
(
parent
)) {
435
if
(
level
[
parent
] !=
lev
)
436
GUM_ERROR
(
FatalError
,
437
"Trying to set level["
438
<<
parent
<<
"] to level="
<<
lev
439
<<
" but already is "
<<
level
[
parent
]);
440
}
else
{
441
level
.
insert
(
parent
,
lev
);
442
}
443
444
if
(
max_level
<
lev
)
max_level
=
lev
;
445
}
446
}
447
}
448
}
449
}
450
Size
levmax
= 0;
451
for
(
const
auto
&
k
:
reversePartialOrder_
) {
452
if
(
k
.
empty
())
break
;
453
levmax
++;
454
}
455
reversePartialOrder_
.
resize
(
levmax
);
456
}
457
458
template
<
typename
GUM_SCALAR
>
459
std
::
vector
<
NodeSet
>
460
ShaferShenoyLIMIDInference
<
GUM_SCALAR
>::
reversePartialOrder
()
const
{
461
return
reversePartialOrder_
;
462
}
463
template
<
typename
GUM_SCALAR
>
464
bool
465
ShaferShenoyLIMIDInference
<
GUM_SCALAR
>::
hasNoForgettingAssumption
()
const
{
466
return
!
noForgettingOrder_
.
empty
();
467
}
468
469
template
<
typename
GUM_SCALAR
>
470
void
ShaferShenoyLIMIDInference
<
GUM_SCALAR
>::
addNoForgettingAssumption
(
471
const
std
::
vector
<
std
::
string
>&
names
) {
472
addNoForgettingAssumption
(
this
->
influenceDiagram
().
ids
(
names
));
473
}
474
475
template
<
typename
GUM_SCALAR
>
476
void
ShaferShenoyLIMIDInference
<
GUM_SCALAR
>::
addNoForgettingAssumption
(
477
const
std
::
vector
<
NodeId
>&
ids
) {
478
const
auto
&
infdiag
=
this
->
influenceDiagram
();
479
for
(
const
auto
node
:
ids
) {
480
if
(!
infdiag
.
exists
(
node
))
GUM_ERROR
(
NotFound
,
node
<<
" is not a NodeId"
);
481
if
(!
infdiag
.
isDecisionNode
(
node
))
482
GUM_ERROR
(
TypeError
,
483
"Node "
<<
node
<<
" ("
<<
infdiag
.
variable
(
node
).
name
()
484
<<
") is not a decision node"
);
485
}
486
if
(
infdiag
.
decisionNodeSize
() !=
ids
.
size
())
487
GUM_ERROR
(
SizeError
,
488
"Some decision nodes are missing in the sequence "
<<
ids
);
489
490
noForgettingOrder_
=
ids
;
491
createReduced_
();
492
}
493
template
<
typename
GUM_SCALAR
>
494
NodeSet
495
ShaferShenoyLIMIDInference
<
GUM_SCALAR
>::
nonRequisiteNodes_
(
NodeId
d
)
const
{
496
const
InfluenceDiagram
<
GUM_SCALAR
>&
infdiag
=
this
->
influenceDiagram
();
497
498
if
(!
infdiag
.
isDecisionNode
(
d
))
499
GUM_ERROR
(
TypeError
,
d
<<
" is not a decision node"
);
500
501
NodeSet
res
;
502
if
(
reduced_
.
parents
(
d
).
empty
())
return
res
;
503
504
NodeSet
descUs
;
505
for
(
const
auto
n
:
reduced_
.
descendants
(
d
))
506
if
(
infdiag
.
isUtilityNode
(
n
))
descUs
.
insert
(
n
);
507
508
NodeSet
cumul
{
descUs
};
509
cumul
<<
d
;
510
auto
g
=
reduced_
.
moralizedAncestralGraph
(
cumul
);
511
512
NodeSet
family
{
reduced_
.
parents
(
d
)};
513
family
<<
d
;
514
bool
notReq
;
515
for
(
const
auto
p
:
reduced_
.
parents
(
d
)) {
516
notReq
=
true
;
517
for
(
const
auto
u
:
descUs
) {
518
if
(
g
.
hasUndirectedPath
(
p
,
u
,
family
)) {
519
notReq
=
false
;
520
break
;
521
}
522
}
523
if
(
notReq
)
res
<<
p
;
524
}
525
return
res
;
526
}
527
528
template
<
typename
GUM_SCALAR
>
529
InfluenceDiagram
<
GUM_SCALAR
>
530
ShaferShenoyLIMIDInference
<
GUM_SCALAR
>::
reducedLIMID
()
const
{
531
const
auto
&
infdiag
=
this
->
influenceDiagram
();
532
InfluenceDiagram
<
GUM_SCALAR
>
res
;
533
for
(
auto
node
:
infdiag
.
nodes
()) {
534
if
(
infdiag
.
isChanceNode
(
node
))
535
res
.
addChanceNode
(
infdiag
.
variable
(
node
),
node
);
536
else
if
(
infdiag
.
isDecisionNode
(
node
))
537
res
.
addDecisionNode
(
infdiag
.
variable
(
node
),
node
);
538
else
// (infdiag.isUtilityNode(node))
539
res
.
addUtilityNode
(
infdiag
.
variable
(
node
),
node
);
540
}
541
542
for
(
const
auto
&
arc
:
reduced_
.
arcs
()) {
543
res
.
addArc
(
arc
.
tail
(),
arc
.
head
());
544
}
545
546
for
(
auto
node
:
infdiag
.
nodes
()) {
547
if
(
infdiag
.
isChanceNode
(
node
))
548
res
.
cpt
(
node
).
fillWith
(
infdiag
.
cpt
(
node
));
549
else
if
(
infdiag
.
isUtilityNode
(
node
))
550
res
.
utility
(
node
).
fillWith
(
infdiag
.
utility
(
node
));
551
}
552
553
// Potentials !!!
554
return
res
;
555
}
556
557
558
template
<
typename
GUM_SCALAR
>
559
const
JunctionTree
*
560
ShaferShenoyLIMIDInference
<
GUM_SCALAR
>::
junctionTree
()
const
{
561
if
(!
isSolvable
()) {
562
GUM_ERROR
(
FatalError
,
"This LIMID/Influence Diagram is not solvable."
)
563
}
564
return
&
reducedJunctionTree_
;
565
}
566
567
template
<
typename
GUM_SCALAR
>
568
void
ShaferShenoyLIMIDInference
<
GUM_SCALAR
>::
collectingMessage_
(
569
PhiNodeProperty
&
phi
,
570
PsiArcProperty
&
psi
,
571
const
NodeId
rootClique
) {
572
const
auto
&
jt
= *
junctionTree
();
573
574
#
ifdef
GUM_SSLI_TRACE_ON
575
GUM_TRACE
(
"COLLECTING TO ROOT "
<<
rootClique
)
576
#
endif
577
std
::
function
<
void
(
NodeId
,
NodeId
) >
parcours
578
= [&](
NodeId
node
,
NodeId
from
) {
579
for
(
const
auto
nei
:
jt
.
neighbours
(
node
)) {
580
if
(
nei
!=
from
)
parcours
(
nei
,
node
);
581
}
582
transmittingMessage_
(
phi
,
psi
,
node
,
from
);
583
};
584
585
for
(
const
auto
nei
:
jt
.
neighbours
(
rootClique
)) {
586
parcours
(
nei
,
rootClique
);
587
}
588
}
589
template
<
typename
GUM_SCALAR
>
590
void
ShaferShenoyLIMIDInference
<
GUM_SCALAR
>::
collectingToFollowingRoot_
(
591
PhiNodeProperty
&
phi
,
592
PsiArcProperty
&
psi
,
593
NodeId
fromClique
,
594
NodeId
toClique
) {
595
const
auto
&
jt
= *
junctionTree
();
596
597
#
ifdef
GUM_SSLI_TRACE_ON
598
GUM_TRACE
(
"COLLECTING TO FOLLOWING ROOT from "
<<
fromClique
<<
" to "
599
<<
toClique
)
600
#
endif
601
std
::
function
<
bool
(
NodeId
,
NodeId
,
NodeId
) >
revparcours
602
= [&](
NodeId
node
,
NodeId
from
,
NodeId
target
) {
603
if
(
node
==
target
)
return
true
;
604
605
bool
found
=
false
;
606
NodeId
prec
;
607
for
(
const
auto
nei
:
jt
.
neighbours
(
node
)) {
608
if
(
nei
!=
from
)
609
if
(
revparcours
(
nei
,
node
,
target
)) {
610
prec
=
nei
;
611
found
=
true
;
612
break
;
613
}
614
}
615
if
(
found
) {
transmittingMessage_
(
phi
,
psi
,
prec
,
node
); }
616
return
found
;
617
};
618
619
revparcours
(
toClique
,
std
::
numeric_limits
<
NodeId
>::
max
(),
fromClique
);
620
}
621
622
template
<
typename
GUM_SCALAR
>
623
void
ShaferShenoyLIMIDInference
<
GUM_SCALAR
>::
deciding_
(
PhiNodeProperty
&
phi
,
624
PsiArcProperty
&
psi
,
625
NodeId
decisionNode
) {
626
const
auto
&
infdiag
=
this
->
influenceDiagram
();
627
const
auto
&
jt
= *
junctionTree
();
628
629
#
ifdef
GUM_SSLI_TRACE_ON
630
GUM_TRACE
(
" Deciding for "
631
<<
infdiag
.
variable
(
decisionNode
).
name
() <<
" in "
632
<<
node_to_clique_
[
decisionNode
] <<
"("
633
<<
infdiag
.
names
(
jt
.
clique
(
node_to_clique_
[
decisionNode
])) <<
")"
)
634
#
endif
635
auto
&
decision
636
=
strategies_
.
getWithDefault
(
decisionNode
,
Potential
<
GUM_SCALAR
>());
637
638
if
(
this
->
hasHardEvidence
(
decisionNode
)) {
639
decision
= *(
this
->
evidence
()[
decisionNode
]);
640
}
else
{
641
DecisionPotential
<
double
>
dp
;
642
dp
=
integrating_
(
phi
,
psi
,
node_to_clique_
[
decisionNode
]);
643
644
SetOfVars
sev
;
645
sev
.
insert
(&
infdiag
.
variable
(
decisionNode
));
646
for
(
const
auto
parent
:
reduced_
.
parents
(
decisionNode
)) {
647
// GUM_TRACE(" parent : " << infdiag.variable(parent).name())
648
sev
.
insert
(&
infdiag
.
variable
(
parent
));
649
}
650
dp
=
dp
^
sev
;
651
652
// SPECIAL CASE FOR DETERMINISTIC DECISION
653
sev
.
erase
(&
infdiag
.
variable
(
decisionNode
));
// only the parents in sev
654
if
(
sev
.
size
() == 0) {
// deterministic decision node
655
unconditionalDecisions_
.
set
(
decisionNode
,
dp
);
656
}
else
if
(
dp
.
probPot
.
margSumIn
(
sev
).
normalize
().
max
()
657
== 1) {
// with deterministic posterior probability
658
// we can use marginalization because we now that dp is deterministic
659
unconditionalDecisions_
.
set
(
660
decisionNode
,
661
DecisionPotential
<
double
>(
dp
.
probPot
.
margSumOut
(
sev
),
662
dp
.
utilPot
.
margSumOut
(
sev
)));
663
}
664
decision
=
dp
.
utilPot
.
putFirst
(&
infdiag
.
variable
(
decisionNode
));
665
666
binarizingMax_
(
decision
,
dp
.
probPot
);
667
}
668
#
ifdef
GUM_SSLI_TRACE_ON
669
GUM_TRACE
(
" UPDATING PHI("
<<
node_to_clique_
[
decisionNode
] <<
")"
)
670
#
ifdef
GUM_SSLI_POTENTIAL_TRACE_ON
671
GUM_TRACE_VAR
(
phi
[
node_to_clique_
[
decisionNode
]])
672
GUM_TRACE_VAR
(
decision
)
673
#
endif
674
#
endif
675
phi
[
node_to_clique_
[
decisionNode
]].
insertProba
(
decision
);
676
#
ifdef
GUM_SSLI_POTENTIAL_TRACE_ON
677
GUM_TRACE_VAR
(
phi
[
node_to_clique_
[
decisionNode
]])
678
#
endif
679
}
680
681
template
<
typename
GUM_SCALAR
>
682
void
ShaferShenoyLIMIDInference
<
GUM_SCALAR
>::
binarizingMax_
(
683
const
Potential
<
GUM_SCALAR
>&
decision
,
684
const
Potential
<
GUM_SCALAR
>&
proba
)
685
const
{
// compute the decisions (as maxEU)
686
Instantiation
I
(
decision
);
687
const
auto
&
firstvar
=
decision
.
variable
(0);
688
for
(
I
.
setFirst
(); !
I
.
end
();
I
.
incNotVar
(
firstvar
)) {
689
I
.
setFirstVar
(
firstvar
);
690
while
(
proba
[
I
] == 0) {
691
I
.
incVar
(
firstvar
);
692
if
(
I
.
end
()) {
// for non valid proba, we keep the first value (by
693
// default)²
694
I
.
setFirstVar
(
firstvar
);
695
break
;
696
}
697
}
698
// we found a non null value of proba
699
Idx
argm
=
I
.
val
(
firstvar
);
700
GUM_SCALAR
umax
=
decision
[
I
];
701
GUM_SCALAR
pmax
=
proba
[
I
];
702
for
(
I
.
incVar
(
firstvar
); !
I
.
end
();
I
.
incVar
(
firstvar
)) {
703
// lexicographical order on (u,p)
704
if
(
proba
[
I
] > 0) {
705
if
((
umax
<
decision
[
I
])
706
|| ((
umax
==
decision
[
I
]) && (
pmax
<
proba
[
I
]))) {
707
umax
=
decision
[
I
];
708
pmax
=
proba
[
I
];
709
argm
=
I
.
val
(
firstvar
);
710
}
711
}
712
}
713
for
(
I
.
setFirstVar
(
firstvar
); !
I
.
end
();
I
.
incVar
(
firstvar
))
714
decision
.
set
(
I
, 0);
715
I
.
chgVal
(
firstvar
,
argm
);
716
decision
.
set
(
I
, 1);
717
}
718
}
719
720
template
<
typename
GUM_SCALAR
>
721
void
ShaferShenoyLIMIDInference
<
GUM_SCALAR
>::
distributingMessage_
(
722
PhiNodeProperty
&
phi
,
723
PsiArcProperty
&
psi
,
724
NodeId
rootClique
) {
725
const
auto
&
jt
= *
junctionTree
();
726
727
std
::
function
<
void
(
NodeId
,
NodeId
) >
parcours
728
= [&](
NodeId
node
,
NodeId
from
) {
729
transmittingFinalMessage_
(
phi
,
psi
,
from
,
node
);
730
phi
.
set
(
node
,
integrating_
(
phi
,
psi
,
node
));
731
for
(
const
auto
nei
:
jt
.
neighbours
(
node
)) {
732
if
(
nei
!=
from
)
parcours
(
nei
,
node
);
733
}
734
};
735
#
ifdef
GUM_SSLI_TRACE_ON
736
GUM_TRACE
(
" DISTRIBUTING"
)
737
#
endif
738
739
// phi.set(rootClique, integrating_(phi, psi, rootClique));
740
for
(
const
auto
nei
:
jt
.
neighbours
(
rootClique
)) {
741
parcours
(
nei
,
rootClique
);
742
}
743
}
744
745
template
<
typename
GUM_SCALAR
>
746
void
ShaferShenoyLIMIDInference
<
GUM_SCALAR
>::
transmittingFinalMessage_
(
747
PhiNodeProperty
&
phi
,
748
PsiArcProperty
&
psi
,
749
NodeId
fromClique
,
750
NodeId
toClique
) {
751
// phi has been updated. No need to integrate anythin
752
psi
.
set
(
Arc
(
fromClique
,
toClique
),
753
phi
[
fromClique
] ^
varsSeparator_
[
Edge
(
fromClique
,
toClique
)]);
754
755
#
ifdef
GUM_SSLI_TRACE_ON
756
const
auto
&
jt
= *
junctionTree
();
757
const
auto
&
infdiag
=
this
->
influenceDiagram
();
758
GUM_TRACE
(
"Computing final message : "
759
<<
fromClique
<<
":"
<<
infdiag
.
names
(
jt
.
clique
(
fromClique
)) <<
"->"
760
<<
toClique
<<
":"
<<
infdiag
.
names
(
jt
.
clique
(
toClique
)))
761
#
ifdef
GUM_SSLI_POTENTIAL_TRACE_ON
762
GUM_TRACE_VAR
(
psi
[
Arc
(
fromClique
,
toClique
)])
763
#
endif
764
#
endif
765
}
766
767
template
<
typename
GUM_SCALAR
>
768
void
ShaferShenoyLIMIDInference
<
GUM_SCALAR
>::
transmittingMessage_
(
769
PhiNodeProperty
&
phi
,
770
PsiArcProperty
&
psi
,
771
NodeId
fromClique
,
772
NodeId
toClique
) {
773
psi
.
set
(
Arc
(
fromClique
,
toClique
),
774
integrating_
(
phi
,
psi
,
fromClique
,
toClique
)
775
^
varsSeparator_
[
Edge
(
fromClique
,
toClique
)]);
776
777
#
ifdef
GUM_SSLI_TRACE_ON
778
const
auto
&
jt
= *
junctionTree
();
779
const
auto
&
infdiag
=
this
->
influenceDiagram
();
780
GUM_TRACE
(
"Computing message : "
781
<<
fromClique
<<
":"
<<
infdiag
.
names
(
jt
.
clique
(
fromClique
)) <<
"->"
782
<<
toClique
<<
":"
<<
infdiag
.
names
(
jt
.
clique
(
toClique
)))
783
784
//# ifdef GUM_SSLI_POTENTIAL_TRACE_ON
785
GUM_TRACE_VAR
(
psi
[
Arc
(
fromClique
,
toClique
)])
786
//# endif
787
#
endif
788
}
789
790
template
<
typename
GUM_SCALAR
>
791
DecisionPotential
<
double
>
792
ShaferShenoyLIMIDInference
<
GUM_SCALAR
>::
integrating_
(
793
const
PhiNodeProperty
&
phi
,
794
const
PsiArcProperty
&
psi
,
795
NodeId
clique
,
796
NodeId
except
)
const
{
797
const
auto
&
jt
= *
junctionTree
();
798
DecisionPotential
<
double
>
res
=
phi
[
clique
];
799
for
(
const
auto
nei
:
jt
.
neighbours
(
clique
))
800
if
(
nei
!=
except
)
res
*=
psi
[
Arc
(
nei
,
clique
)];
801
802
#
ifdef
GUM_SSLI_TRACE_ON
803
const
auto
&
infdiag
=
this
->
influenceDiagram
();
804
GUM_TRACE
(
" Integrating into : "
<<
clique
<<
":"
805
<<
infdiag
.
names
(
jt
.
clique
(
clique
)))
806
807
#
ifdef
GUM_SSLI_POTENTIAL_TRACE_ON
808
GUM_TRACE_VAR
(
phi
[
clique
])
809
GUM_TRACE_VAR
(
res
)
810
#
endif
811
#
endif
812
return
res
;
813
}
814
815
template
<
typename
GUM_SCALAR
>
816
DecisionPotential
<
double
>
817
ShaferShenoyLIMIDInference
<
GUM_SCALAR
>::
integrating_
(
818
const
PhiNodeProperty
&
phi
,
819
const
PsiArcProperty
&
psi
,
820
NodeId
clique
)
const
{
821
const
auto
&
jt
= *
junctionTree
();
822
DecisionPotential
<
double
>
res
=
phi
[
clique
];
823
824
#
ifdef
GUM_SSLI_TRACE_ON
825
const
auto
&
infdiag
=
this
->
influenceDiagram
();
826
GUM_TRACE
(
" Full-Integrating into : "
<<
clique
<<
":"
827
<<
infdiag
.
names
(
jt
.
clique
(
clique
)))
828
#
endif
829
830
for
(
const
auto
nei
:
jt
.
neighbours
(
clique
))
831
res
*=
psi
[
Arc
(
nei
,
clique
)];
832
833
#
ifdef
GUM_SSLI_TRACE_ON
834
GUM_TRACE
(
" After Full-Integrating into : "
835
<<
clique
<<
":"
<<
infdiag
.
names
(
jt
.
clique
(
clique
)))
836
#
endif
837
838
839
#
ifdef
GUM_SSLI_TRACE_ON
840
#
ifdef
GUM_SSLI_POTENTIAL_TRACE_ON
841
GUM_TRACE_VAR
(
res
)
842
#
endif
843
#
endif
844
845
return
res
;
846
}
847
848
template
<
typename
GUM_SCALAR
>
849
void
ShaferShenoyLIMIDInference
<
GUM_SCALAR
>::
computingPosteriors_
(
850
const
PhiNodeProperty
&
phi
,
851
const
PsiArcProperty
&
psi
) {
852
NodeProperty
<
DecisionPotential
<
double
> >
finalphis
;
853
854
const
auto
&
infdiag
=
this
->
influenceDiagram
();
855
posteriors_
.
clear
();
856
strategies_
.
clear
();
857
for
(
const
auto
node
:
infdiag
.
nodes
()) {
858
#
ifdef
GUM_SSLI_TRACE_ON
859
GUM_TRACE
(
" ---> Posterior for variable "
<<
infdiag
.
variable
(
node
).
name
())
860
GUM_TRACE_VAR
(
phi
[
node_to_clique_
[
node
]])
861
#
endif
862
const
auto
clik
=
node_to_clique_
[
node
];
863
if
(!
finalphis
.
exists
(
clik
))
864
finalphis
.
insert
(
clik
,
integrating_
(
phi
,
psi
,
clik
));
865
const
auto
&
finalphi
=
finalphis
[
clik
];
866
867
auto
&
res
868
=
posteriors_
.
getWithDefault
(
node
,
DecisionPotential
<
GUM_SCALAR
>());
869
if
(
infdiag
.
isChanceNode
(
node
)) {
870
SetOfVars
sev
;
871
sev
.
insert
(&
infdiag
.
variable
(
node
));
872
res
=
finalphi
^
sev
;
873
res
.
probPot
.
normalize
();
874
}
else
if
(
infdiag
.
isDecisionNode
(
node
)) {
875
SetOfVars
sev
;
876
sev
.
insert
(&
infdiag
.
variable
(
node
));
877
SetOfVars
family
=
sev
;
878
for
(
const
auto
par
:
reduced_
.
parents
(
node
)) {
879
if
(
infdiag
.
isChanceNode
(
par
))
family
.
insert
(&
infdiag
.
variable
(
par
));
880
}
881
const
auto
dp
=
finalphi
^
family
;
882
883
#
ifdef
GUM_SSLI_TRACE_ON
884
GUM_TRACE_VAR
(
node_to_clique_
[
node
])
885
#
ifdef
GUM_SSLI_POTENTIAL_TRACE_ON
886
GUM_TRACE_VAR
(
finalphi
)
887
888
GUM_TRACE_VAR
(
dp
)
889
#
endif
890
#
endif
891
892
gum
::
Potential
<
double
>
decision
893
=
dp
.
utilPot
.
putFirst
(&
infdiag
.
variable
(
node
));
894
binarizingMax_
(
decision
,
dp
.
probPot
);
895
strategies_
.
insert
(
node
,
decision
);
896
res
=
dp
^
sev
;
897
res
.
probPot
.
normalize
();
898
if
(
unconditionalDecisions_
.
exists
(
node
)) {
899
res
.
utilPot
=
unconditionalDecisions_
[
node
].
utilPot
;
900
}
901
}
else
{
// utility
902
SetOfVars
family
;
903
904
family
.
insert
(&
infdiag
.
variable
(
node
));
905
for
(
const
auto
par
:
reduced_
.
parents
(
node
)) {
906
family
.
insert
(&
infdiag
.
variable
(
par
));
907
}
908
res
=
finalphi
^
family
;
909
910
res
.
probPot
.
normalize
();
911
res
.
utilPot
=
infdiag
.
utility
(
node
);
912
}
913
}
914
}
915
916
template
<
typename
GUM_SCALAR
>
917
const
Potential
<
GUM_SCALAR
>&
918
ShaferShenoyLIMIDInference
<
GUM_SCALAR
>::
posterior
(
NodeId
node
) {
919
return
posteriors_
[
node
].
probPot
;
920
}
921
922
template
<
typename
GUM_SCALAR
>
923
const
Potential
<
GUM_SCALAR
>&
924
ShaferShenoyLIMIDInference
<
GUM_SCALAR
>::
posteriorUtility
(
NodeId
node
) {
925
return
posteriors_
[
node
].
utilPot
;
926
}
927
928
929
template
<
typename
GUM_SCALAR
>
930
std
::
pair
<
GUM_SCALAR
,
GUM_SCALAR
>
931
ShaferShenoyLIMIDInference
<
GUM_SCALAR
>::
meanVar
(
NodeId
node
) {
932
return
posteriors_
[
node
].
meanVar
();
933
}
934
935
}
/* namespace gum */
936
937
#
endif
/* DOXYGEN_SHOULD_SKIP_THIS */
gum::Set::emplace
INLINE void emplace(Args &&... args)
Definition:
set_tpl.h:669