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