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