aGrUM
0.20.2
a C++ library for (probabilistic) graphical models
jointTargetedMNInference_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 Implementation of the non pure virtual methods of class
25
* JointTargetedMNInference.
26
*/
27
#
include
<
agrum
/
MN
/
inference
/
tools
/
jointTargetedMNInference
.
h
>
28
#
include
<
agrum
/
tools
/
variables
/
rangeVariable
.
h
>
29
30
namespace
gum
{
31
32
33
// Default Constructor
34
template
<
typename
GUM_SCALAR >
35
JointTargetedMNInference< GUM_SCALAR >::JointTargetedMNInference(
36
const
IMarkovNet< GUM_SCALAR >* mn) :
37
MarginalTargetedMNInference< GUM_SCALAR >(mn) {
38
// assign a MN if this has not been done before (due to virtual inheritance)
39
if
(
this
->hasNoModel_()) {
40
MarkovNetInference< GUM_SCALAR >::setMarkovNetDuringConstruction__(mn);
41
}
42
GUM_CONSTRUCTOR(JointTargetedMNInference);
43
}
44
45
46
// Destructor
47
template
<
typename
GUM_SCALAR
>
48
JointTargetedMNInference
<
GUM_SCALAR
>::~
JointTargetedMNInference
() {
49
GUM_DESTRUCTOR
(
JointTargetedMNInference
);
50
}
51
52
53
// assigns a new MN to the inference engine
54
template
<
typename
GUM_SCALAR
>
55
void
JointTargetedMNInference
<
GUM_SCALAR
>::
onModelChanged_
(
56
const
GraphicalModel
*
mn
) {
57
MarginalTargetedMNInference
<
GUM_SCALAR
>::
onModelChanged_
(
mn
);
58
onAllJointTargetsErased_
();
59
joint_targets__
.
clear
();
60
}
61
62
63
// ##############################################################################
64
// Targets
65
// ##############################################################################
66
67
// return true if target is a nodeset target.
68
template
<
typename
GUM_SCALAR
>
69
INLINE
bool
JointTargetedMNInference
<
GUM_SCALAR
>::
isJointTarget
(
70
const
NodeSet
&
vars
)
const
{
71
if
(
this
->
hasNoModel_
())
72
GUM_ERROR
(
NullElement
,
73
"No Markov net has been assigned to the "
74
"inference algorithm"
);
75
76
const
auto
&
gra
=
this
->
MN
().
graph
();
77
for
(
const
auto
var
:
vars
) {
78
if
(!
gra
.
exists
(
var
)) {
79
GUM_ERROR
(
UndefinedElement
,
80
var
<<
" is not a NodeId in the Markov network"
);
81
}
82
}
83
84
return
joint_targets__
.
contains
(
vars
);
85
}
86
87
88
// Clear all previously defined single targets
89
template
<
typename
GUM_SCALAR
>
90
INLINE
void
JointTargetedMNInference
<
GUM_SCALAR
>::
eraseAllMarginalTargets
() {
91
MarginalTargetedMNInference
<
GUM_SCALAR
>::
eraseAllTargets
();
92
}
93
94
95
// Clear all previously defined targets (single targets and sets of targets)
96
template
<
typename
GUM_SCALAR
>
97
INLINE
void
JointTargetedMNInference
<
GUM_SCALAR
>::
eraseAllJointTargets
() {
98
if
(
joint_targets__
.
size
() > 0) {
99
// we already are in target mode. So no this->setTargetedMode_(); is needed
100
onAllJointTargetsErased_
();
101
joint_targets__
.
clear
();
102
this
->
setState_
(
103
MarkovNetInference
<
GUM_SCALAR
>::
StateOfInference
::
OutdatedStructure
);
104
}
105
}
106
107
108
// Clear all previously defined targets (single and joint targets)
109
template
<
typename
GUM_SCALAR
>
110
INLINE
void
JointTargetedMNInference
<
GUM_SCALAR
>::
eraseAllTargets
() {
111
eraseAllMarginalTargets
();
112
eraseAllJointTargets
();
113
}
114
115
116
// Add a set of nodes as a new target
117
template
<
typename
GUM_SCALAR
>
118
void
JointTargetedMNInference
<
GUM_SCALAR
>::
addJointTarget
(
119
const
NodeSet
&
joint_target
) {
120
// check if the nodes in the target belong to the Markov network
121
if
(
this
->
hasNoModel_
())
122
GUM_ERROR
(
NullElement
,
123
"No Markov net has been assigned to the "
124
"inference algorithm"
);
125
126
const
auto
&
dag
=
this
->
MN
().
graph
();
127
for
(
const
auto
node
:
joint_target
) {
128
if
(!
dag
.
exists
(
node
)) {
129
GUM_ERROR
(
UndefinedElement
,
130
"at least one one in "
<<
joint_target
131
<<
" does not belong to the mn"
);
132
}
133
}
134
135
if
(
isExactJointComputable_
(
joint_target
))
return
;
136
if
(!
superForJointComputable_
(
joint_target
).
empty
())
return
;
137
138
// check if joint_target is a subset of an already existing target
139
for
(
const
auto
&
target
:
joint_targets__
) {
140
if
(
target
.
isProperSupersetOf
(
joint_target
))
return
;
141
}
142
143
// check if joint_target is not a superset of an already existing target
144
// in this case, we need to remove old existing target
145
for
(
auto
iter
=
joint_targets__
.
beginSafe
();
146
iter
!=
joint_targets__
.
endSafe
();
147
++
iter
) {
148
if
(
iter
->
isProperSubsetOf
(
joint_target
))
eraseJointTarget
(*
iter
);
149
}
150
151
this
->
setTargetedMode_
();
// does nothing if already in targeted mode
152
joint_targets__
.
insert
(
joint_target
);
153
onJointTargetAdded_
(
joint_target
);
154
this
->
setState_
(
155
MarkovNetInference
<
GUM_SCALAR
>::
StateOfInference
::
OutdatedStructure
);
156
}
157
158
159
// removes an existing set target
160
template
<
typename
GUM_SCALAR
>
161
void
JointTargetedMNInference
<
GUM_SCALAR
>::
eraseJointTarget
(
162
const
NodeSet
&
joint_target
) {
163
// check if the nodes in the target belong to the Markov network
164
if
(
this
->
hasNoModel_
())
165
GUM_ERROR
(
NullElement
,
166
"No Markov net has been assigned to the "
167
"inference algorithm"
);
168
169
const
auto
&
dag
=
this
->
MN
().
graph
();
170
for
(
const
auto
node
:
joint_target
) {
171
if
(!
dag
.
exists
(
node
)) {
172
GUM_ERROR
(
UndefinedElement
,
173
"at least one one in "
<<
joint_target
174
<<
" does not belong to the mn"
);
175
}
176
}
177
178
// check that the joint_target set does not contain the new target
179
if
(
joint_targets__
.
contains
(
joint_target
)) {
180
// note that we have to be in target mode when we are here
181
// so, no this->setTargetedMode_(); is necessary
182
onJointTargetErased_
(
joint_target
);
183
joint_targets__
.
erase
(
joint_target
);
184
this
->
setState_
(
185
MarkovNetInference
<
GUM_SCALAR
>::
StateOfInference
::
OutdatedStructure
);
186
}
187
}
188
189
190
/// returns the list of target sets
191
template
<
typename
GUM_SCALAR
>
192
INLINE
const
Set
<
NodeSet
>&
193
JointTargetedMNInference
<
GUM_SCALAR
>::
jointTargets
()
const
noexcept
{
194
return
joint_targets__
;
195
}
196
197
/// returns the number of target sets
198
template
<
typename
GUM_SCALAR
>
199
INLINE
Size
200
JointTargetedMNInference
<
GUM_SCALAR
>::
nbrJointTargets
()
const
noexcept
{
201
return
joint_targets__
.
size
();
202
}
203
204
205
// ##############################################################################
206
// Inference
207
// ##############################################################################
208
209
// Compute the posterior of a nodeset.
210
template
<
typename
GUM_SCALAR
>
211
const
Potential
<
GUM_SCALAR
>&
212
JointTargetedMNInference
<
GUM_SCALAR
>::
jointPosterior
(
const
NodeSet
&
nodes
) {
213
// try to get the smallest set of targets that contains "nodes"
214
bool
found_exact_target
=
false
;
215
NodeSet
super_target
;
216
217
if
(
isExactJointComputable_
(
nodes
)) {
218
found_exact_target
=
true
;
219
}
else
{
220
super_target
=
superForJointComputable_
(
nodes
);
221
if
(
super_target
.
empty
()) {
222
GUM_ERROR
(
UndefinedElement
,
223
"No joint target containing "
224
<<
nodes
<<
" could be found among "
<<
joint_targets__
);
225
}
226
}
227
228
if
(!
this
->
isInferenceDone
()) {
this
->
makeInference
(); }
229
230
if
(
found_exact_target
)
231
return
jointPosterior_
(
nodes
);
232
else
233
return
jointPosterior_
(
nodes
,
super_target
);
234
}
235
236
237
// Compute the posterior of a node
238
template
<
typename
GUM_SCALAR
>
239
const
Potential
<
GUM_SCALAR
>&
240
JointTargetedMNInference
<
GUM_SCALAR
>::
posterior
(
NodeId
node
) {
241
if
(
this
->
isTarget
(
node
))
242
return
MarginalTargetedMNInference
<
GUM_SCALAR
>::
posterior
(
node
);
243
else
244
return
jointPosterior
(
NodeSet
{
node
});
245
}
246
247
// Compute the posterior of a node
248
template
<
typename
GUM_SCALAR
>
249
const
Potential
<
GUM_SCALAR
>&
JointTargetedMNInference
<
GUM_SCALAR
>::
posterior
(
250
const
std
::
string
&
nodeName
) {
251
return
posterior
(
this
->
MN
().
idFromName
(
nodeName
));
252
}
253
254
// ##############################################################################
255
// Entropy
256
// ##############################################################################
257
template
<
typename
GUM_SCALAR
>
258
GUM_SCALAR
JointTargetedMNInference
<
GUM_SCALAR
>::
I
(
const
std
::
string
&
Xname
,
259
const
std
::
string
&
Yname
) {
260
return
I
(
this
->
MN
().
idFromName
(
Xname
),
this
->
MN
().
idFromName
(
Yname
));
261
}
262
263
template
<
typename
GUM_SCALAR
>
264
GUM_SCALAR
JointTargetedMNInference
<
GUM_SCALAR
>::
VI
(
const
std
::
string
&
Xname
,
265
const
std
::
string
&
Yname
) {
266
return
VI
(
this
->
MN
().
idFromName
(
Xname
),
this
->
MN
().
idFromName
(
Yname
));
267
}
268
269
270
/* Mutual information between X and Y
271
* @see http://en.wikipedia.org/wiki/Mutual_information
272
*
273
* @warning Due to limitation of @joint, may not be able to compute this value
274
* @throw OperationNotAllowed in these cases
275
*/
276
template
<
typename
GUM_SCALAR
>
277
GUM_SCALAR
JointTargetedMNInference
<
GUM_SCALAR
>::
I
(
NodeId
X
,
NodeId
Y
) {
278
Potential
<
GUM_SCALAR
>
pX
,
pY
, *
pXY
=
nullptr
;
279
if
(
X
==
Y
) {
280
GUM_ERROR
(
OperationNotAllowed
,
"Mutual Information I(X,Y) with X==Y"
);
281
}
282
283
try
{
284
// here use unnormalized joint posterior rather than just posterior
285
// to avoid saving the posterior in the cache of the inference engines
286
// like LazyPropagation or Shafer-Shenoy.
287
pXY
=
this
->
unnormalizedJointPosterior_
({
X
,
Y
});
288
pXY
->
normalize
();
289
pX
=
pXY
->
margSumOut
({&(
this
->
MN
().
variable
(
Y
))});
290
pY
=
pXY
->
margSumOut
({&(
this
->
MN
().
variable
(
X
))});
291
}
catch
(...) {
292
if
(
pXY
!=
nullptr
) {
delete
pXY
; }
293
throw
;
294
}
295
296
Instantiation
i
(*
pXY
);
297
auto
res
= (
GUM_SCALAR
)0;
298
299
for
(
i
.
setFirst
(); !
i
.
end
(); ++
i
) {
300
GUM_SCALAR
vXY
= (*
pXY
)[
i
];
301
GUM_SCALAR
vX
=
pX
[
i
];
302
GUM_SCALAR
vY
=
pY
[
i
];
303
304
if
(
vXY
> (
GUM_SCALAR
)0) {
305
if
(
vX
== (
GUM_SCALAR
)0 ||
vY
== (
GUM_SCALAR
)0) {
306
GUM_ERROR
(
OperationNotAllowed
,
307
"Mutual Information (X,Y) with P(X)=0 or P(Y)=0 "
308
"and P(X,Y)>0"
);
309
}
310
311
res
+=
vXY
* (
std
::
log2
(
vXY
) -
std
::
log2
(
vX
) -
std
::
log2
(
vY
));
312
}
313
}
314
315
delete
pXY
;
316
317
return
res
;
318
}
319
320
321
/** Variation of information between X and Y
322
* @see http://en.wikipedia.org/wiki/Variation_of_information
323
*
324
* @warning Due to limitation of @joint, may not be able to compute this value
325
* @throw OperationNotAllowed in these cases
326
*/
327
template
<
typename
GUM_SCALAR
>
328
INLINE
GUM_SCALAR
JointTargetedMNInference
<
GUM_SCALAR
>::
VI
(
NodeId
X
,
329
NodeId
Y
) {
330
return
this
->
H
(
X
) +
this
->
H
(
Y
) - 2 *
I
(
X
,
Y
);
331
}
332
333
334
template
<
typename
GUM_SCALAR
>
335
Potential
<
GUM_SCALAR
>
336
JointTargetedMNInference
<
GUM_SCALAR
>::
evidenceJointImpact
(
337
const
NodeSet
&
targets
,
338
const
NodeSet
&
evs
) {
339
if
(!(
evs
*
targets
).
empty
()) {
340
GUM_ERROR
(
InvalidArgument
,
341
"Targets ("
<<
targets
<<
") can not intersect evs ("
<<
evs
342
<<
")."
);
343
}
344
auto
condset
=
this
->
MN
().
minimalCondSet
(
targets
,
evs
);
345
346
this
->
eraseAllTargets
();
347
this
->
eraseAllEvidence
();
348
349
Instantiation
iTarget
;
350
Potential
<
GUM_SCALAR
>
res
;
351
for
(
const
auto
&
target
:
targets
) {
352
res
.
add
(
this
->
MN
().
variable
(
target
));
353
iTarget
.
add
(
this
->
MN
().
variable
(
target
));
354
}
355
this
->
addJointTarget
(
targets
);
356
357
for
(
const
auto
&
n
:
condset
) {
358
res
.
add
(
this
->
MN
().
variable
(
n
));
359
this
->
addEvidence
(
n
, 0);
360
}
361
362
Instantiation
inst
(
res
);
363
for
(
inst
.
setFirstOut
(
iTarget
); !
inst
.
end
();
inst
.
incOut
(
iTarget
)) {
364
// inferring
365
for
(
const
auto
&
n
:
condset
)
366
this
->
chgEvidence
(
n
,
inst
.
val
(
this
->
MN
().
variable
(
n
)));
367
this
->
makeInference
();
368
// populate res
369
for
(
inst
.
setFirstIn
(
iTarget
); !
inst
.
end
();
inst
.
incIn
(
iTarget
)) {
370
res
.
set
(
inst
,
this
->
jointPosterior
(
targets
)[
inst
]);
371
}
372
inst
.
setFirstIn
(
iTarget
);
// remove inst.end() flag
373
}
374
375
return
res
;
376
}
377
378
template
<
typename
GUM_SCALAR
>
379
Potential
<
GUM_SCALAR
>
380
JointTargetedMNInference
<
GUM_SCALAR
>::
evidenceJointImpact
(
381
const
std
::
vector
<
std
::
string
>&
targets
,
382
const
std
::
vector
<
std
::
string
>&
evs
) {
383
const
auto
&
mn
=
this
->
MN
();
384
return
evidenceJointImpact
(
mn
.
nodeset
(
targets
),
mn
.
nodeset
(
evs
));
385
}
386
387
388
template
<
typename
GUM_SCALAR
>
389
GUM_SCALAR
JointTargetedMNInference
<
GUM_SCALAR
>::
jointMutualInformation
(
390
const
NodeSet
&
targets
) {
391
const
auto
&
mn
=
this
->
MN
();
392
const
Size
siz
=
targets
.
size
();
393
if
(
siz
<= 1) {
394
GUM_ERROR
(
InvalidArgument
,
395
"jointMutualInformation needs at least 2 variables (targets="
396
<<
targets
<<
")"
);
397
}
398
399
this
->
eraseAllTargets
();
400
this
->
eraseAllEvidence
();
401
this
->
addJointTarget
(
targets
);
402
this
->
makeInference
();
403
const
auto
po
=
this
->
jointPosterior
(
targets
);
404
405
gum
::
Instantiation
caracteristic
;
406
gum
::
Instantiation
variables
;
407
for
(
const
auto
nod
:
targets
) {
408
const
auto
&
var
=
mn
.
variable
(
nod
);
409
auto
pv
=
new
gum
::
RangeVariable
(
var
.
name
(),
""
, 0, 1);
410
caracteristic
.
add
(*
pv
);
411
variables
.
add
(
var
);
412
}
413
414
Set
<
const
DiscreteVariable
* >
sov
;
415
416
const
GUM_SCALAR
start
= (
siz
% 2 == 0) ?
GUM_SCALAR
(-1.0) :
GUM_SCALAR
(1.0);
417
GUM_SCALAR
sign
;
418
GUM_SCALAR
res
=
GUM_SCALAR
(0.0);
419
420
caracteristic
.
setFirst
();
421
for
(
caracteristic
.
inc
(); !
caracteristic
.
end
();
caracteristic
.
inc
()) {
422
sov
.
clear
();
423
sign
=
start
;
424
for
(
Idx
i
= 0;
i
<
caracteristic
.
nbrDim
();
i
++) {
425
if
(
caracteristic
.
val
(
i
) == 1) {
426
sign
= -
sign
;
427
sov
.
insert
(&
variables
.
variable
(
i
));
428
}
429
}
430
res
+=
sign
*
po
.
margSumIn
(
sov
).
entropy
();
431
}
432
433
for
(
Idx
i
= 0;
i
<
caracteristic
.
nbrDim
();
i
++) {
434
delete
&
caracteristic
.
variable
(
i
);
435
}
436
437
return
res
;
438
}
439
440
template
<
typename
GUM_SCALAR
>
441
GUM_SCALAR
JointTargetedMNInference
<
GUM_SCALAR
>::
jointMutualInformation
(
442
const
std
::
vector
<
std
::
string
>&
targets
) {
443
return
jointMutualInformation
(
this
->
MN
().
nodeset
(
targets
));
444
}
445
446
template
<
typename
GUM_SCALAR
>
447
bool
JointTargetedMNInference
<
GUM_SCALAR
>::
isExactJointComputable_
(
448
const
NodeSet
&
vars
) {
449
if
(
joint_targets__
.
contains
(
vars
))
return
true
;
450
451
return
false
;
452
}
453
454
template
<
typename
GUM_SCALAR
>
455
NodeSet
JointTargetedMNInference
<
GUM_SCALAR
>::
superForJointComputable_
(
456
const
NodeSet
&
vars
) {
457
for
(
const
auto
&
target
:
joint_targets__
)
458
if
(
vars
.
isProperSubsetOf
(
target
))
return
target
;
459
460
for
(
const
auto
&
factor
:
this
->
MN
().
factors
())
461
if
(
vars
.
isProperSubsetOf
(
factor
.
first
))
return
factor
.
first
;
462
463
return
NodeSet
();
464
}
465
466
}
/* namespace gum */
gum::Set::emplace
INLINE void emplace(Args &&... args)
Definition:
set_tpl.h:669