aGrUM
0.20.2
a C++ library for (probabilistic) graphical models
defaultJunctionTreeStrategy.cpp
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
/** @file
23
* @brief An algorithms producing a junction given the elimination tree
24
* produced by the triangulation algorithm
25
*
26
* @author Christophe GONZALES(@AMU) and Pierre-Henri WUILLEMIN(@LIP6)
27
*/
28
29
#
include
<
numeric
>
30
31
#
include
<
agrum
/
agrum
.
h
>
32
#
include
<
agrum
/
tools
/
graphs
/
algorithms
/
triangulations
/
junctionTreeStrategies
/
defaultJunctionTreeStrategy
.
h
>
33
#
include
<
agrum
/
tools
/
graphs
/
algorithms
/
triangulations
/
staticTriangulation
.
h
>
34
35
namespace
gum
{
36
37
// default constructor
38
DefaultJunctionTreeStrategy
::
DefaultJunctionTreeStrategy
() {
39
// for debugging purposes
40
GUM_CONSTRUCTOR
(
DefaultJunctionTreeStrategy
);
41
}
42
43
// copy constructor
44
DefaultJunctionTreeStrategy
::
DefaultJunctionTreeStrategy
(
45
const
DefaultJunctionTreeStrategy
&
from
) :
46
JunctionTreeStrategy
(
from
),
47
has_junction_tree__
(
from
.
has_junction_tree__
),
48
junction_tree__
(
from
.
junction_tree__
),
49
node_2_junction_clique__
(
from
.
node_2_junction_clique__
) {
50
// for debugging purposes
51
GUM_CONS_CPY
(
DefaultJunctionTreeStrategy
);
52
}
53
54
// move constructor
55
DefaultJunctionTreeStrategy
::
DefaultJunctionTreeStrategy
(
56
DefaultJunctionTreeStrategy
&&
from
) :
57
JunctionTreeStrategy
(
std
::
move
(
from
)),
58
has_junction_tree__
(
from
.
has_junction_tree__
),
59
junction_tree__
(
std
::
move
(
from
.
junction_tree__
)),
60
node_2_junction_clique__
(
std
::
move
(
from
.
node_2_junction_clique__
)) {
61
// for debugging purposes
62
GUM_CONS_MOV
(
DefaultJunctionTreeStrategy
);
63
}
64
65
// destructor
66
DefaultJunctionTreeStrategy
::~
DefaultJunctionTreeStrategy
() {
67
// for debugging purposes
68
GUM_DESTRUCTOR
(
DefaultJunctionTreeStrategy
);
69
}
70
71
// clone factory
72
DefaultJunctionTreeStrategy
*
DefaultJunctionTreeStrategy
::
newFactory
()
const
{
73
return
new
DefaultJunctionTreeStrategy
;
74
}
75
76
// virtual copy constructor
77
DefaultJunctionTreeStrategy
*
78
DefaultJunctionTreeStrategy
::
copyFactory
(
StaticTriangulation
*
tr
)
const
{
79
if
(
tr
==
nullptr
) {
80
return
new
DefaultJunctionTreeStrategy
(*
this
);
81
}
else
{
82
// here, we need to assign new triangulation "tr" to the new strategy
83
84
// if there was already a triangulation associated with the current
85
// strategy, 2 cases can obtain:
86
// 1/ both triangulation__ and tr point toward the same graph to be
87
// triangulated. In this case, no need to recompute anything
88
// 2/ they point toward different graphs. Then, we must indicate that
89
// the new strategy has not computed anything yet
90
if
((
triangulation_
!=
nullptr
)
91
&& (
tr
->
originalGraph
() ==
triangulation_
->
originalGraph
())) {
92
auto
new_strategy
=
new
DefaultJunctionTreeStrategy
(*
this
);
// case 1/
93
new_strategy
->
triangulation_
=
tr
;
94
return
new_strategy
;
95
}
else
{
// case 2/
96
auto
new_strategy
=
new
DefaultJunctionTreeStrategy
;
97
new_strategy
->
setTriangulation
(
tr
);
98
return
new_strategy
;
99
}
100
}
101
}
102
103
// indicates whether the junction tree strategy needs fill-ins to work
104
// properly
105
bool
DefaultJunctionTreeStrategy
::
requiresFillIns
()
const
{
return
false
; }
106
107
// assign the triangulation to the junction tree strategy
108
void
DefaultJunctionTreeStrategy
::
setTriangulation
(
StaticTriangulation
*
tr
) {
109
clear
();
110
triangulation_
=
tr
;
111
}
112
113
// returns, for each node, the clique which was created by its deletion
114
const
NodeProperty
<
NodeId
>&
DefaultJunctionTreeStrategy
::
createdCliques
() {
115
// compute the junction tree only if it does not already exist
116
if
(!
has_junction_tree__
)
computeJunctionTree__
();
117
118
return
node_2_junction_clique__
;
119
}
120
121
// returns the Id of the clique created by the elimination of a given
122
// node during the triangulation process */
123
NodeId
DefaultJunctionTreeStrategy
::
createdClique
(
const
NodeId
id
) {
124
// compute the junction tree only if it does not already exist
125
if
(!
has_junction_tree__
)
computeJunctionTree__
();
126
127
return
node_2_junction_clique__
[
id
];
128
}
129
130
// returns the junction tree asked by the triangulation
131
const
CliqueGraph
&
DefaultJunctionTreeStrategy
::
junctionTree
() {
132
// compute the junction tree only if it does not already exist
133
if
(!
has_junction_tree__
)
computeJunctionTree__
();
134
135
return
junction_tree__
;
136
}
137
138
// resets the current junction tree strategy data structures
139
void
DefaultJunctionTreeStrategy
::
clear
() {
140
has_junction_tree__
=
false
;
141
junction_tree__
.
clear
();
142
node_2_junction_clique__
.
clear
();
143
}
144
145
// computes a junction tree from an elimination tree
146
void
DefaultJunctionTreeStrategy
::
computeJunctionTree__
() {
147
// if no triangulation is assigned to the strategy, we cannot compute
148
// the junction tree, so raise an exception
149
if
(
triangulation_
==
nullptr
)
150
GUM_ERROR
(
UndefinedElement
,
151
"No triangulation has been assigned to "
152
"the DefaultJunctionTreeStrategy"
);
153
154
// copy the elimination tree into the junction tree
155
junction_tree__
=
triangulation_
->
eliminationTree
();
156
157
// mark all the edges of the junction tree to false
158
EdgeProperty
<
bool
>
mark
=
junction_tree__
.
edgesProperty
(
false
);
159
160
// create a vector indicating by which clique a given clique has been
161
// substituted during the transformation from the elimination tree to the
162
// junction tree. Assume that clique J of the elmination tree has been
163
// substituted by clique K of the elimination tree, and that clique J
164
// (resp. K) was the jth (resp. kth) one created during the triangulation
165
// process. Then, in the vector below, substitution[j] = k.
166
const
std
::
vector
<
NodeId
>&
elim_order
=
triangulation_
->
eliminationOrder
();
167
168
auto
size
=
elim_order
.
size
();
169
std
::
vector
<
NodeId
>
substitution
(
size
);
170
std
::
iota
(
substitution
.
begin
(),
substitution
.
end
(), 0);
171
172
// for all cliques C_i, from the last one created to the first one, check
173
// if there exists a parent C_j (a neighbor created before C_i) such that
174
// |C_j| = |C_i| + 1 and such that the edge is not marked. In this case,
175
// this means that C_j contains C_i. Hence, we should remove C_i, and link
176
// all of its neighbors to C_j. These links will be marked to true as no
177
// such neighbor can be included in C_j (and conversely).
178
if
(
size
> 0) {
179
for
(
auto
i
=
size
;
i
>= 1; --
i
) {
180
const
NodeId
C_i
=
NodeId
(
i
- 1);
181
const
auto
card_C_i_plus_1
=
junction_tree__
.
clique
(
C_i
).
size
() + 1;
182
183
// search for C_j such that |C_j| = [C_i| + 1
184
NodeId
C_j
=
C_i
;
185
186
for
(
const
auto
C_jj
:
junction_tree__
.
neighbours
(
C_i
))
187
if
((
C_i
>
C_jj
) && !
mark
[
Edge
(
C_jj
,
C_i
)]
188
&& (
junction_tree__
.
clique
(
C_jj
).
size
() ==
card_C_i_plus_1
)) {
189
// ok, here we found a parent such that |C_jj| = [C_i| + 1
190
C_j
=
C_jj
;
191
junction_tree__
.
eraseEdge
(
Edge
(
C_j
,
C_i
));
192
break
;
193
}
194
195
// if we found a C_j, link the neighbors of C_i to C_j
196
if
(
C_j
!=
C_i
) {
197
for
(
const
auto
nei
:
junction_tree__
.
neighbours
(
C_i
)) {
198
junction_tree__
.
addEdge
(
C_j
,
nei
);
199
mark
.
insert
(
Edge
(
C_j
,
nei
),
true
);
200
}
201
202
// remove C_i
203
substitution
[
C_i
] =
C_j
;
204
junction_tree__
.
eraseNode
(
C_i
);
205
}
206
}
207
}
208
209
// compute the transitive closure of vector substitution
210
for
(
std
::
size_t
i
= 0;
i
<
size
; ++
i
)
211
substitution
[
i
] =
substitution
[
substitution
[
i
]];
212
213
// using the transitive closure of vector substitution, compute for each
214
// node the clique of the junction tree that was created by its
215
// elimination during the triangulation
216
for
(
NodeId
i
=
NodeId
(0);
i
<
size
; ++
i
) {
217
node_2_junction_clique__
.
insert
(
elim_order
[
i
],
substitution
[
i
]);
218
}
219
220
has_junction_tree__
=
true
;
221
}
222
223
}
/* namespace gum */
gum::Set::emplace
INLINE void emplace(Args &&... args)
Definition:
set_tpl.h:669