aGrUM
0.21.0
a C++ library for (probabilistic) graphical models
incrementalTriangulation.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 source implementations for computing default triangulations of graphs
24
*
25
* @author Christophe GONZALES(@AMU) and Pierre-Henri WUILLEMIN(@LIP6)
26
*/
27
28
#
include
<
limits
>
29
#
include
<
utility
>
30
31
#
include
<
agrum
/
agrum
.
h
>
32
#
include
<
agrum
/
tools
/
core
/
math
/
math_utils
.
h
>
33
34
#
include
<
agrum
/
tools
/
core
/
list
.
h
>
35
#
include
<
agrum
/
tools
/
graphs
/
algorithms
/
triangulations
/
incrementalTriangulation
.
h
>
36
#
include
<
agrum
/
tools
/
graphs
/
undiGraph
.
h
>
37
38
#
ifdef
GUM_NO_INLINE
39
#
include
<
agrum
/
tools
/
graphs
/
algorithms
/
triangulations
/
incrementalTriangulation_inl
.
h
>
40
#
endif
// GUM_NO_INLINE
41
42
#
ifndef
DOXYGEN_SHOULD_SKIP_THIS
43
44
namespace
gum
{
45
46
/// constructor
47
IncrementalTriangulation
::
IncrementalTriangulation
(
const
UnconstrainedTriangulation
&
triang_algo
,
48
const
UndiGraph
*
theGraph
,
49
const
NodeProperty
<
Size
>*
domsizes
) :
50
Triangulation
(
domsizes
),
51
_triangulation_
(
triang_algo
.
newFactory
()) {
52
GUM_CONSTRUCTOR
(
IncrementalTriangulation
);
53
54
// reset the triangulation algorithm => it starts with an empty graph
55
_triangulation_
->
clear
();
56
57
// copy the graph passed in argument and update the structures
58
// containing the informations useful for the triangulation
59
60
for
(
const
auto
node
: *
theGraph
)
61
addNode
(
node
, (*
domsizes
)[
node
]);
62
63
// insert all the edges of the graph into the structure. This will
64
// implicitly update the "require_update" field
65
for
(
const
auto
&
edge
:
theGraph
->
edges
())
66
addEdge
(
edge
.
first
(),
edge
.
second
());
67
}
68
69
/// default constructor
70
IncrementalTriangulation
::
IncrementalTriangulation
(
71
const
UnconstrainedTriangulation
&
triang_algo
) :
72
_triangulation_
(
triang_algo
.
newFactory
()) {
73
GUM_CONSTRUCTOR
(
IncrementalTriangulation
);
74
75
// reset the triangulation algorithm => it starts with an empty graph
76
_triangulation_
->
clear
();
77
}
78
79
/// copy operator
80
IncrementalTriangulation
::
IncrementalTriangulation
(
const
IncrementalTriangulation
&
from
) :
81
Triangulation
(
from
),
_graph_
(
from
.
_graph_
),
_junction_tree_
(
from
.
_junction_tree_
),
82
_T_mpd_
(
from
.
_T_mpd_
),
_mps_of_node_
(
from
.
_mps_of_node_
),
83
_cliques_of_mps_
(
from
.
_cliques_of_mps_
),
_mps_of_clique_
(
from
.
_mps_of_clique_
),
84
_mps_affected_
(
from
.
_mps_affected_
),
_triangulation_
(
from
.
_triangulation_
->
newFactory
()),
85
_require_update_
(
from
.
_require_update_
),
86
_require_elimination_order_
(
from
.
_require_elimination_order_
),
87
_elimination_order_
(
from
.
_elimination_order_
),
88
_reverse_elimination_order_
(
from
.
_reverse_elimination_order_
),
89
_require_created_JT_cliques_
(
from
.
_require_created_JT_cliques_
),
90
_created_JT_cliques_
(
from
.
_created_JT_cliques_
) {
91
GUM_CONS_CPY
(
IncrementalTriangulation
);
92
93
_domain_sizes_
=
from
.
_domain_sizes_
;
94
}
95
96
/// destructor
97
IncrementalTriangulation
::~
IncrementalTriangulation
() {
98
GUM_DESTRUCTOR
(
IncrementalTriangulation
);
99
100
// remove things that were allocated within the current class
101
delete
_triangulation_
;
102
}
103
104
/// virtual clone constructor
105
IncrementalTriangulation
*
IncrementalTriangulation
::
newFactory
()
const
{
106
return
new
IncrementalTriangulation
(*
_triangulation_
);
107
}
108
109
/// virtual copy constructor
110
IncrementalTriangulation
*
IncrementalTriangulation
::
copyFactory
()
const
{
111
return
new
IncrementalTriangulation
(*
this
);
112
}
113
114
/// copy operator
115
IncrementalTriangulation
&
116
IncrementalTriangulation
::
operator
=(
const
IncrementalTriangulation
&
from
) {
117
// avoid self assignment
118
if
(
this
!= &
from
) {
119
GUM_OP_CPY
(
IncrementalTriangulation
)
120
121
// copy all the structures stored in "from"
122
_graph_
=
from
.
_graph_
;
123
_domain_sizes_
=
from
.
_domain_sizes_
;
124
_junction_tree_
=
from
.
_junction_tree_
;
125
_T_mpd_
=
from
.
_T_mpd_
;
126
_mps_of_node_
=
from
.
_mps_of_node_
;
127
_cliques_of_mps_
=
from
.
_cliques_of_mps_
;
128
_mps_of_clique_
=
from
.
_mps_of_clique_
;
129
_mps_affected_
=
from
.
_mps_affected_
;
130
_require_update_
=
from
.
_require_update_
;
131
_require_elimination_order_
=
from
.
_require_elimination_order_
;
132
_elimination_order_
=
from
.
_elimination_order_
;
133
_reverse_elimination_order_
=
from
.
_reverse_elimination_order_
;
134
_require_created_JT_cliques_
=
from
.
_require_created_JT_cliques_
;
135
_created_JT_cliques_
=
from
.
_created_JT_cliques_
;
136
137
// just in case we changed the triangulation algorithm, we remove it
138
// and create it again
139
delete
_triangulation_
;
140
_triangulation_
=
from
.
_triangulation_
->
newFactory
();
141
}
142
143
return
*
this
;
144
}
145
146
/// adds a new node to the graph (and update the internal structures)
147
void
IncrementalTriangulation
::
addNode
(
const
NodeId
node
,
Size
modal
) {
148
// check if the node already exists
149
if
(
_graph_
.
existsNode
(
node
))
return
;
150
151
// add the new node to the graph
152
_graph_
.
addNodeWithId
(
node
);
153
_domain_sizes_
.
insert
(
node
,
modal
);
154
155
// add a new clique to T_mpd and the junction tree
156
NodeSet
clique_nodes
(2);
157
clique_nodes
.
insert
(
node
);
158
159
NodeId
MPS
=
_T_mpd_
.
addNode
(
clique_nodes
);
160
NodeId
new_clique
=
_junction_tree_
.
addNode
(
clique_nodes
);
161
162
// indicate in which MPS node belongs
163
List
<
NodeId
>&
list_of_mps
=
_mps_of_node_
.
insert
(
node
,
List
<
NodeId
>()).
second
;
164
list_of_mps
.
insert
(
MPS
);
165
166
// indicate in which MPS the clique added to the junction tree belongs
167
std
::
vector
<
NodeId
>&
cliquesMPS
168
=
_cliques_of_mps_
.
insert
(
MPS
,
std
::
vector
<
NodeId
>()).
second
;
169
170
cliquesMPS
.
push_back
(
new_clique
);
171
_mps_of_clique_
.
insert
(
new_clique
,
MPS
);
172
173
// indicate that the new MPS should not be affected by a triangulation
174
_mps_affected_
.
insert
(
MPS
,
false
);
175
176
// insert the node into the elimination order sequence
177
_elimination_order_
.
push_back
(
node
);
178
179
if
(!
_reverse_elimination_order_
.
exists
(
node
))
180
_reverse_elimination_order_
.
insert
(
node
,
Size
(
_elimination_order_
.
size
()));
181
182
if
(!
_created_JT_cliques_
.
exists
(
node
))
_created_JT_cliques_
.
insert
(
node
,
new_clique
);
183
}
184
185
/// mark the mps affected by the deletion of a given edge
186
187
void
IncrementalTriangulation
::
_markAffectedMPSsByRemoveLink_
(
const
NodeId
My
,
188
const
NodeId
Mz
,
189
const
Edge
&
edge
) {
190
// mark the MPS so that it will be retriangulated
191
_mps_affected_
[
My
] =
true
;
192
193
// mark all the neighbour MPS that contain edge
194
for
(
const
auto
nei
:
_T_mpd_
.
neighbours
(
My
))
195
if
(
nei
!=
Mz
) {
196
const
NodeSet
&
Syk
=
_T_mpd_
.
separator
(
Edge
(
nei
,
My
));
197
198
if
(
Syk
.
contains
(
edge
.
first
()) &&
Syk
.
contains
(
edge
.
second
()))
199
_markAffectedMPSsByRemoveLink_
(
nei
,
My
,
edge
);
200
}
201
}
202
203
/// removes an edge from the graph (the join tree may need a retriangulation)
204
205
void
IncrementalTriangulation
::
eraseEdge
(
const
Edge
&
edge
) {
206
// check that the edge exist
207
if
(!
_graph_
.
existsEdge
(
edge
))
return
;
208
209
// find a MPS containing the edge (X,Y)
210
const
NodeId
X
=
edge
.
first
();
211
const
NodeId
Y
=
edge
.
second
();
212
213
const
List
<
NodeId
>&
mps1
=
_mps_of_node_
[
X
];
214
const
List
<
NodeId
>&
mps2
=
_mps_of_node_
[
Y
];
215
216
NodeId
Mx
=
mps1
[0];
217
218
if
(
mps1
.
size
() <=
mps2
.
size
()) {
219
for
(
const
auto
node
:
mps1
)
220
if
(
_T_mpd_
.
clique
(
node
).
contains
(
Y
)) {
221
Mx
=
node
;
222
break
;
223
}
224
}
else
{
225
for
(
const
auto
node
:
mps2
)
226
if
(
_T_mpd_
.
clique
(
node
).
contains
(
X
)) {
227
Mx
=
node
;
228
break
;
229
}
230
}
231
232
// mark the MPS that need be updated
233
_markAffectedMPSsByRemoveLink_
(
Mx
,
Mx
,
edge
);
234
235
_require_update_
=
true
;
236
237
_require_elimination_order_
=
true
;
238
239
_require_created_JT_cliques_
=
true
;
240
241
// remove the edge (X,Y) from the graph
242
_graph_
.
eraseEdge
(
edge
);
243
}
244
245
/// removes a node from the graph (the join tree may need a triangulation
246
/// update)
247
248
void
IncrementalTriangulation
::
eraseNode
(
const
NodeId
X
) {
249
// check if the node exists
250
if
(!
_graph_
.
existsNode
(
X
))
return
;
251
252
// remove all the edges adjacent to the node
253
{
254
const
NodeSet
&
neighbours
=
_graph_
.
neighbours
(
X
);
255
256
for
(
auto
neighbour_edge
=
neighbours
.
beginSafe
();
// safe iterator needed here
257
neighbour_edge
!=
neighbours
.
endSafe
();
258
++
neighbour_edge
) {
259
eraseEdge
(
Edge
(*
neighbour_edge
,
X
));
260
}
261
}
262
263
auto
&
MPS_of_X
=
_mps_of_node_
[
X
];
264
265
// remove X from the MPS containing X
266
for
(
const
auto
node
:
MPS_of_X
) {
267
_T_mpd_
.
eraseFromClique
(
node
,
X
);
268
269
// if the intersection between *iter and one of its neighbour is empty,
270
// remove the edge linking them
271
auto
&
neighbours
=
_T_mpd_
.
neighbours
(
node
);
272
273
for
(
auto
it_neighbour
=
neighbours
.
beginSafe
();
it_neighbour
!=
neighbours
.
endSafe
();
274
++
it_neighbour
) {
// safe iterator needed here
275
Edge
neigh
(*
it_neighbour
,
node
);
276
277
if
(
_T_mpd_
.
separator
(
neigh
).
size
() == 0)
_T_mpd_
.
eraseEdge
(
neigh
);
278
}
279
}
280
281
// remove X from the cliques containing X
282
for
(
const
auto
clique
:
MPS_of_X
) {
283
const
std
::
vector
<
NodeId
>&
cliques_of_X
=
_cliques_of_mps_
[
clique
];
284
285
for
(
unsigned
int
i
= 0;
i
<
cliques_of_X
.
size
(); ++
i
) {
286
_junction_tree_
.
eraseFromClique
(
cliques_of_X
[
i
],
X
);
287
288
// if the intersection between clique and one of its neighbour is empty,
289
// remove the edge linking them only if, in addition, there is no
290
// edge in _graph_ between a node of clique and a node in the neighbour
291
auto
&
neighbours
=
_junction_tree_
.
neighbours
(
cliques_of_X
[
i
]);
292
293
for
(
auto
it_neighbour
=
neighbours
.
beginSafe
();
it_neighbour
!=
neighbours
.
endSafe
();
294
++
it_neighbour
) {
// safe iterator needed here
295
Edge
neigh
(*
it_neighbour
,
cliques_of_X
[
i
]);
296
297
if
(
_junction_tree_
.
separator
(
neigh
).
size
() == 0) {
298
// try to see if there is an edge between the nodes of one extremity
299
// of *neighbour and those of the other extremity
300
bool
hasCommonEdge
=
false
;
301
302
for
(
const
auto
node1
:
_junction_tree_
.
clique
(
neigh
.
first
()))
303
for
(
const
auto
node2
:
_junction_tree_
.
clique
(
neigh
.
second
()))
304
if
(
_graph_
.
existsEdge
(
node1
,
node2
)) {
305
hasCommonEdge
=
true
;
306
break
;
307
}
308
309
if
(!
hasCommonEdge
) {
_junction_tree_
.
eraseEdge
(
neigh
); }
310
}
311
}
312
}
313
}
314
315
// if the MPS containing X is empty, then remove it, as well as the
316
// corresponding clique in the junction tree (which also only contains X)
317
if
((
MPS_of_X
.
size
() == 1) && (
_T_mpd_
.
clique
(
MPS_of_X
[0]).
size
() == 0)) {
318
_junction_tree_
.
eraseNode
(
_cliques_of_mps_
[
MPS_of_X
[0]][0]);
319
_T_mpd_
.
eraseNode
(
MPS_of_X
[0]);
320
_mps_of_clique_
.
erase
(
_cliques_of_mps_
[
MPS_of_X
[0]][0]);
321
_cliques_of_mps_
.
erase
(
MPS_of_X
[0]);
322
_mps_affected_
.
erase
(
MPS_of_X
[0]);
323
}
324
325
_mps_of_node_
.
erase
(
X
);
326
327
// update the elimination orders
328
329
if
(!
_require_update_
) {
330
for
(
Idx
i
=
_reverse_elimination_order_
[
X
] + 1;
i
<
_reverse_elimination_order_
.
size
(); ++
i
)
331
_elimination_order_
[
i
- 1] =
_elimination_order_
[
i
];
332
333
_elimination_order_
.
pop_back
();
334
335
_reverse_elimination_order_
.
erase
(
X
);
336
337
_created_JT_cliques_
.
erase
(
X
);
338
}
339
340
// remove X completely from the graph
341
_graph_
.
eraseNode
(
X
);
342
343
_domain_sizes_
.
erase
(
X
);
344
}
345
346
/// add a new edge to the graph
347
348
int
IncrementalTriangulation
::
_markAffectedMPSsByAddLink_
(
const
NodeId
Mx
,
349
const
NodeId
Mz
,
350
const
NodeId
X
,
351
const
NodeId
Y
) {
352
// check if Mx contains Y. In this case, mark Mx and return 1 to indicate
353
// that
354
// Y has been found or 2 to indicate that Y has been found and that the
355
// nearest
356
// MPS containing X has been marked
357
const
NodeSet
&
cliqueMX
=
_T_mpd_
.
clique
(
Mx
);
358
359
if
(
cliqueMX
.
contains
(
Y
)) {
360
_mps_affected_
[
Mx
] =
true
;
361
362
if
(
cliqueMX
.
contains
(
X
))
return
2;
363
364
return
1;
365
}
366
367
// parse Mx's neighbours until we find Y
368
for
(
const
auto
other_node
:
_T_mpd_
.
neighbours
(
Mx
))
369
if
(
other_node
!=
Mz
) {
370
int
neighbourStatus
=
_markAffectedMPSsByAddLink_
(
other_node
,
Mx
,
X
,
Y
);
371
372
if
(
neighbourStatus
== 2)
373
return
2;
374
else
if
(
neighbourStatus
== 1) {
375
_mps_affected_
[
Mx
] =
true
;
376
377
if
(
cliqueMX
.
contains
(
X
))
return
2;
378
379
return
1;
380
}
381
}
382
383
// indicate that X was not found
384
return
0;
385
}
386
387
/// adds a new edge to the graph (the join tree may need a triangulation
388
/// update)
389
void
IncrementalTriangulation
::
addEdge
(
const
NodeId
X
,
const
NodeId
Y
) {
390
// check that the edge exist
391
if
((
X
==
Y
) || !
_graph_
.
existsNode
(
X
) || !
_graph_
.
existsNode
(
Y
)
392
||
_graph_
.
existsEdge
(
Edge
(
X
,
Y
)))
393
return
;
394
395
// add the edge to the graph
396
_graph_
.
addEdge
(
X
,
Y
);
397
398
// take any MPS containing X and search its tree to find Y
399
NodeId
&
mps_X
=
_mps_of_node_
[
X
][0];
400
401
int
found
=
_markAffectedMPSsByAddLink_
(
mps_X
,
mps_X
,
X
,
Y
);
402
403
if
(
found
== 0) {
404
// the mps containing X do not belong to the same tree as those containing
405
// Y
406
NodeId
&
mps_Y
=
_mps_of_node_
[
Y
][0];
407
408
// find a clique in mps_X containing X and another in mps_Y containing Y
409
// and add a clique XY to the junction tree linked to the cliques found
410
// in mps_X and mps_Y
411
const
std
::
vector
<
NodeId
>&
cliques_X
=
_cliques_of_mps_
[
mps_X
];
412
const
std
::
vector
<
NodeId
>&
cliques_Y
=
_cliques_of_mps_
[
mps_Y
];
413
NodeId
c_X
= 0,
c_Y
= 0;
414
415
for
(
unsigned
int
i
= 0;
i
<
cliques_X
.
size
(); ++
i
) {
416
if
(
_junction_tree_
.
clique
(
cliques_X
[
i
]).
contains
(
X
)) {
417
c_X
=
cliques_X
[
i
];
418
break
;
419
}
420
}
421
422
for
(
unsigned
int
i
= 0;
i
<
cliques_Y
.
size
(); ++
i
) {
423
if
(
_junction_tree_
.
clique
(
cliques_Y
[
i
]).
contains
(
Y
)) {
424
c_Y
=
cliques_Y
[
i
];
425
break
;
426
}
427
}
428
429
// link c_Y and c_X through a new node containing XY
430
NodeSet
nodes
(2);
431
432
nodes
.
insert
(
X
);
433
434
nodes
.
insert
(
Y
);
435
436
NodeId
newNode
=
_junction_tree_
.
addNode
(
nodes
);
437
438
_junction_tree_
.
addEdge
(
newNode
,
c_X
);
439
_junction_tree_
.
addEdge
(
newNode
,
c_Y
);
440
441
NodeId
newMPS
=
_T_mpd_
.
addNode
(
nodes
);
442
443
_T_mpd_
.
addEdge
(
newMPS
,
mps_X
);
444
_T_mpd_
.
addEdge
(
newMPS
,
mps_Y
);
445
446
// check that the maximal prime subgraph containing X is not X alone
447
// in this case, remove this max prime subgraph, as well as the
448
// corresponding
449
// clique in the junction tree
450
if
(
_T_mpd_
.
clique
(
mps_X
).
size
() == 1) {
451
_junction_tree_
.
eraseNode
(
c_X
);
452
_T_mpd_
.
eraseNode
(
mps_X
);
453
_mps_affected_
.
erase
(
mps_X
);
454
_mps_of_clique_
.
erase
(
c_X
);
455
_cliques_of_mps_
.
erase
(
mps_X
);
456
_created_JT_cliques_
[
X
] =
newNode
;
457
mps_X
=
newMPS
;
458
}
else
459
_mps_of_node_
[
X
].
insert
(
newMPS
);
460
461
// do the same thing as above for node Y
462
if
(
_T_mpd_
.
clique
(
mps_Y
).
size
() == 1) {
463
_junction_tree_
.
eraseNode
(
c_Y
);
464
_T_mpd_
.
eraseNode
(
mps_Y
);
465
_mps_affected_
.
erase
(
mps_Y
);
466
_mps_of_clique_
.
erase
(
c_Y
);
467
_cliques_of_mps_
.
erase
(
mps_Y
);
468
_created_JT_cliques_
[
Y
] =
newNode
;
469
mps_Y
=
newMPS
;
470
}
else
471
_mps_of_node_
[
Y
].
insert
(
newMPS
);
472
473
std
::
vector
<
NodeId
>&
cl
=
_cliques_of_mps_
.
insert
(
newMPS
,
std
::
vector
<
NodeId
>()).
second
;
474
475
cl
.
push_back
(
newNode
);
476
477
_mps_of_clique_
.
insert
(
newNode
,
newMPS
);
478
479
_mps_affected_
.
insert
(
newMPS
,
false
);
480
}
else
{
481
_require_update_
=
true
;
482
_require_created_JT_cliques_
=
true
;
483
}
484
485
// in all cases, recompute the elimination ordering
486
_require_elimination_order_
=
true
;
487
}
488
489
/// checks that the incremental triangulation works properly
490
491
bool
IncrementalTriangulation
::
_check_
() {
492
// just in case, update everything
493
updateTriangulation
();
494
495
bool
OK
=
true
;
496
497
// check that all the nodes of the graph belong to the junction tree
498
{
499
NodeProperty
<
bool
>
nodesProp
=
_graph_
.
nodesProperty
<
bool
>(
false
);
500
501
for
(
const
auto
cliq
:
_junction_tree_
.
nodes
())
502
for
(
const
auto
node
:
_junction_tree_
.
clique
(
cliq
))
503
nodesProp
[
node
] =
true
;
504
505
for
(
const
auto
&
elt
:
nodesProp
)
506
if
(!
elt
.
second
) {
507
std
::
cerr
<<
"check nodes"
<<
std
::
endl
508
<<
_graph_
<<
std
::
endl
509
<<
_junction_tree_
<<
std
::
endl
;
510
OK
=
false
;
511
}
512
513
if
(!
OK
)
return
false
;
514
}
515
516
// check that the edgs belong to the junction tree
517
{
518
std
::
pair
<
NodeId
,
NodeId
>
thePair
;
519
EdgeProperty
<
bool
>
edgesProp
=
_graph_
.
edgesProperty
(
false
);
520
521
for
(
const
auto
cliq
:
_junction_tree_
.
nodes
()) {
522
const
NodeSet
&
clique
=
_junction_tree_
.
clique
(
cliq
);
523
524
for
(
auto
iter2
=
clique
.
begin
();
iter2
!=
clique
.
end
(); ++
iter2
) {
525
auto
iter3
=
iter2
;
526
527
for
(++
iter3
;
iter3
!=
clique
.
end
(); ++
iter3
) {
528
thePair
.
first
=
std
::
min
(*
iter2
, *
iter3
);
529
thePair
.
second
=
std
::
max
(*
iter2
, *
iter3
);
530
531
if
(
_graph_
.
existsEdge
(
thePair
.
first
,
thePair
.
second
))
532
edgesProp
[
Edge
(
thePair
.
first
,
thePair
.
second
)] =
true
;
533
}
534
}
535
}
536
537
for
(
const
auto
&
elt
:
edgesProp
)
538
if
(!
elt
.
second
) {
539
std
::
cerr
<<
"check edges"
<<
std
::
endl
540
<<
_graph_
<<
std
::
endl
541
<<
_junction_tree_
<<
std
::
endl
;
542
OK
=
false
;
543
}
544
545
if
(!
OK
)
return
false
;
546
}
547
548
// check that all the nodes of the graph belong to the MPS tree
549
{
550
NodeProperty
<
bool
>
nodesProp
=
_graph_
.
nodesProperty
<
bool
>(
false
);
551
552
for
(
const
auto
cliq
:
_T_mpd_
.
nodes
())
553
for
(
const
auto
node
:
_T_mpd_
.
clique
(
cliq
))
554
nodesProp
[
node
] =
true
;
555
556
for
(
const
auto
&
elt
:
nodesProp
)
557
if
(!
elt
.
second
) {
558
std
::
cerr
<<
"check nodes"
<<
std
::
endl
<<
_graph_
<<
std
::
endl
<<
_T_mpd_
<<
std
::
endl
;
559
OK
=
false
;
560
}
561
562
if
(!
OK
)
return
false
;
563
}
564
565
// check that the arcs of the graph belong to the MPS tree
566
{
567
std
::
pair
<
NodeId
,
NodeId
>
thePair
;
568
EdgeProperty
<
bool
>
edgesProp
=
_graph_
.
edgesProperty
(
false
);
569
570
for
(
const
auto
cliq
:
_T_mpd_
.
nodes
()) {
571
const
NodeSet
&
clique
=
_T_mpd_
.
clique
(
cliq
);
572
573
for
(
auto
iter2
=
clique
.
begin
();
iter2
!=
clique
.
end
(); ++
iter2
) {
574
auto
iter3
=
iter2
;
575
576
for
(++
iter3
;
iter3
!=
clique
.
end
(); ++
iter3
) {
577
thePair
.
first
=
std
::
min
(*
iter2
, *
iter3
);
578
thePair
.
second
=
std
::
max
(*
iter2
, *
iter3
);
579
580
if
(
_graph_
.
existsEdge
(
thePair
.
first
,
thePair
.
second
))
581
edgesProp
[
Edge
(
thePair
.
first
,
thePair
.
second
)] =
true
;
582
}
583
}
584
}
585
586
for
(
const
auto
&
elt
:
edgesProp
)
587
if
(!
elt
.
second
) {
588
std
::
cerr
<<
"check edges"
<<
std
::
endl
<<
_graph_
<<
std
::
endl
<<
_T_mpd_
<<
std
::
endl
;
589
OK
=
false
;
590
}
591
592
if
(!
OK
)
return
false
;
593
}
594
595
// check the MPS of node
596
{
597
NodeProperty
<
NodeProperty
<
bool
> >
chk
;
598
599
for
(
const
auto
node
:
_graph_
.
nodes
())
600
chk
.
insert
(
node
,
HashTable
<
NodeId
,
bool
>());
601
602
for
(
const
auto
cliq
:
_T_mpd_
.
nodes
())
603
for
(
auto
node
:
_T_mpd_
.
clique
(
cliq
))
604
chk
[
node
].
insert
(
cliq
,
false
);
605
606
for
(
const
auto
&
elt
:
_mps_of_node_
) {
607
HashTable
<
NodeId
,
bool
>&
hash
=
chk
[
elt
.
first
];
608
609
for
(
const
auto
cell
:
elt
.
second
) {
610
if
(!
hash
.
exists
(
cell
)) {
611
std
::
cerr
<<
"check mps of nodes"
<<
std
::
endl
612
<<
_T_mpd_
<<
std
::
endl
613
<<
_mps_of_node_
<<
std
::
endl
;
614
OK
=
false
;
615
}
616
617
hash
[
cell
] =
true
;
618
}
619
}
620
621
for
(
const
auto
&
elt
:
chk
)
622
for
(
const
auto
&
elt2
:
elt
.
second
)
623
if
(!
elt2
.
second
) {
624
std
::
cerr
<<
"check mps of nodes2"
<<
std
::
endl
625
<<
_T_mpd_
<<
std
::
endl
626
<<
_mps_of_node_
<<
std
::
endl
;
627
OK
=
false
;
628
}
629
630
if
(!
OK
)
return
false
;
631
}
632
633
// check that the junction tree and the T_mpd are junction trees
634
{
635
if
(!
_junction_tree_
.
isJoinTree
()) {
636
std
::
cerr
<<
"check join tree _junction_tree_"
<<
std
::
endl
637
<<
_junction_tree_
<<
std
::
endl
;
638
return
false
;
639
}
640
641
if
(!
_T_mpd_
.
isJoinTree
()) {
642
std
::
cerr
<<
"check join tree _T_mpd_"
<<
std
::
endl
<<
_T_mpd_
<<
std
::
endl
;
643
return
false
;
644
}
645
}
646
647
// check elimination sequences
648
{
649
eliminationOrder
();
650
651
if
(
_elimination_order_
.
size
() !=
_graph_
.
size
()) {
652
std
::
cerr
<<
"check elimination order"
<<
std
::
endl
<<
_elimination_order_
<<
std
::
endl
;
653
return
false
;
654
}
655
656
NodeSet
nodes
;
657
658
for
(
const
auto
node
:
_graph_
.
nodes
()) {
659
if
(
nodes
.
exists
(
node
)) {
660
std
::
cerr
<<
"check elimination order"
<<
std
::
endl
<<
_elimination_order_
<<
std
::
endl
;
661
return
false
;
662
}
else
663
nodes
.
insert
(
node
);
664
}
665
666
if
(
nodes
.
size
() !=
_graph_
.
size
()) {
667
std
::
cerr
<<
"check elimination order"
<<
std
::
endl
<<
_elimination_order_
<<
std
::
endl
;
668
return
false
;
669
}
670
671
if
(
_reverse_elimination_order_
.
size
() !=
_graph_
.
size
()) {
672
std
::
cerr
<<
"check reverse elimination order"
<<
std
::
endl
673
<<
_reverse_elimination_order_
<<
std
::
endl
;
674
return
false
;
675
}
676
677
for
(
const
auto
node
:
_graph_
.
nodes
()) {
678
if
(!
_reverse_elimination_order_
.
exists
(
node
)) {
679
std
::
cerr
<<
"check reverse elimination order"
<<
std
::
endl
680
<<
_reverse_elimination_order_
<<
std
::
endl
;
681
return
false
;
682
}
683
}
684
}
685
686
// check created junction tree cliques
687
{
688
createdJunctionTreeCliques
();
689
690
if
(
_created_JT_cliques_
.
size
() !=
_graph_
.
size
()) {
691
std
::
cerr
<<
"check creating JT cliques"
<<
std
::
endl
<<
_created_JT_cliques_
<<
std
::
endl
;
692
return
false
;
693
}
694
695
for
(
const
auto
node
:
_graph_
.
nodes
()) {
696
if
(!
_created_JT_cliques_
.
exists
(
node
)
697
|| !
_junction_tree_
.
existsNode
(
_created_JT_cliques_
[
node
])) {
698
std
::
cerr
<<
"check created JT cliques"
<<
std
::
endl
<<
_created_JT_cliques_
<<
std
::
endl
;
699
return
false
;
700
}
701
}
702
}
703
704
return
true
;
705
}
706
707
/// set up a connected subgraph to be triangulated
708
709
void
IncrementalTriangulation
::
_setUpConnectedTriangulation_
(
710
NodeId
Mx
,
711
NodeId
Mfrom
,
712
UndiGraph
&
theGraph
,
713
std
::
vector
<
Edge
>&
notAffectedneighbourCliques
,
714
HashTable
<
NodeId
,
bool
>&
cliques_affected
) {
715
// mark the clique so that we won't try to update it several times
716
cliques_affected
[
Mx
] =
false
;
717
718
// get the nodes that are concerned by the triangulation update
719
for
(
const
auto
node
:
_junction_tree_
.
clique
(
Mx
))
720
if
(!
theGraph
.
exists
(
node
))
theGraph
.
addNodeWithId
(
node
);
721
722
// go on with the neighbour cliques in the junction tree
723
for
(
const
auto
othernode
:
_junction_tree_
.
neighbours
(
Mx
))
724
if
(
othernode
!=
Mfrom
) {
725
if
(
cliques_affected
.
exists
(
othernode
)) {
726
_setUpConnectedTriangulation_
(
othernode
,
727
Mx
,
728
theGraph
,
729
notAffectedneighbourCliques
,
730
cliques_affected
);
731
}
else
{
732
// indicate that we have a clique not affected that is adjacent
733
// to an affected one
734
notAffectedneighbourCliques
.
push_back
(
Edge
(
othernode
,
Mx
));
735
}
736
}
737
}
738
739
/// update the junction tree
740
741
void
IncrementalTriangulation
::
_updateJunctionTree_
(
NodeProperty
<
bool
>&
all_cliques_affected
,
742
NodeSet
&
new_nodes_in_junction_tree
) {
743
// a temporary subgraph in which we actually perform the triangulation
744
UndiGraph
tmp_graph
;
745
746
// for each triangulation, we will keep track of the cliques of the
747
// junction tree that are not affected by the triangulation but that are
748
// adjacent to cliques affected. This will enable us to connect easily the
749
// newly created cliques with the old ones.
750
std
::
vector
<
Edge
>
notAffectedneighbourCliques
;
751
752
// parse all the affected MPS and get the corresponding cliques
753
754
for
(
const
auto
&
elt
:
_mps_affected_
)
755
if
(
elt
.
second
) {
756
// get the cliques contained in this MPS
757
const
std
::
vector
<
NodeId
>&
cliques
=
_cliques_of_mps_
[
elt
.
first
];
758
759
for
(
unsigned
int
i
= 0;
i
<
cliques
.
size
(); ++
i
)
760
all_cliques_affected
.
insert
(
cliques
[
i
],
true
);
761
}
762
763
// for each connected set of cliques involved in the triangulations
764
// perform a new triangulation and update the max prime subgraph tree
765
for
(
const
auto
&
elt
:
all_cliques_affected
) {
766
if
(
elt
.
second
) {
767
// set up the connected subgraph that need be retriangulated and the
768
// cliques that are affected by this triangulation
769
tmp_graph
.
clear
();
770
notAffectedneighbourCliques
.
clear
();
771
_setUpConnectedTriangulation_
(
elt
.
first
,
772
elt
.
first
,
773
tmp_graph
,
774
notAffectedneighbourCliques
,
775
all_cliques_affected
);
776
777
// insert the edges in tmp_graph
778
for
(
auto
edge
:
_graph_
.
edges
()) {
779
try
{
780
tmp_graph
.
addEdge
(
edge
.
first
(),
edge
.
second
());
781
}
catch
(
Exception
&) {}
// both extremities must be in tmp_graph
782
}
783
784
// remove from the mps_of_node table the affected mps containing the
785
// node
786
// for ( UndiGraph::NodeIterator iter_node =
787
// tmp_graph.beginNodes();iter_node
788
// != tmp_graph.endNodes(); ++iter_node ) {
789
for
(
const
auto
node
:
tmp_graph
.
nodes
()) {
790
List
<
NodeId
>&
mps
=
_mps_of_node_
[
node
];
791
792
for
(
HashTableConstIteratorSafe
<
NodeId
,
bool
>
iter_mps
793
=
_mps_affected_
.
beginSafe
();
// safe iterator needed here
794
iter_mps
!=
_mps_affected_
.
endSafe
();
795
++
iter_mps
) {
796
if
(
iter_mps
.
val
())
mps
.
eraseByVal
(
iter_mps
.
key
());
797
}
798
}
799
800
// now tmp_graph contains the graph that should be triangulated.
801
// so triangulate it and get its junction tree
802
_triangulation_
->
setGraph
(&
tmp_graph
, &
_domain_sizes_
);
803
804
const
CliqueGraph
&
tmp_junction_tree
=
_triangulation_
->
junctionTree
();
805
806
// now, update the global junction tree
807
// first add the nodes of tmp_junction_tree to _junction_tree_
808
// to do so, store the translations of the node ids of tmp_junction_tree
809
// into the node ids of _junction_tree_
810
NodeProperty
<
NodeId
>
tmp2global_junction_tree
(
tmp_junction_tree
.
size
());
811
812
for
(
const
auto
cliq
:
tmp_junction_tree
.
nodes
()) {
813
// get new ids for the nodes of tmp_junction_tree. These should be
814
// greater than or equal to _junction_tree_.bound () so that we can
815
// use the max_old_id defined at the beginning of the method.
816
NodeId
new_id
=
_junction_tree_
.
addNode
(
tmp_junction_tree
.
clique
(
cliq
));
817
// translate the id of the temprary JT into an id of the global JT
818
tmp2global_junction_tree
.
insert
(
cliq
,
new_id
);
819
new_nodes_in_junction_tree
.
insert
(
new_id
);
820
}
821
822
// and add the edges of tmp_junction_tree to _junction_tree_
823
for
(
const
auto
&
edge
:
tmp_junction_tree
.
edges
())
824
_junction_tree_
.
addEdge
(
tmp2global_junction_tree
[
edge
.
first
()],
825
tmp2global_junction_tree
[
edge
.
second
()]);
826
827
// second get the edges in _junction_tree_ that have an extremal clique
828
// R
829
// in the affected clique set and the other one S not in the affected
830
// set
831
// and see which new node V in the _junction_tree_ should be connected
832
// to S. The running intersection property guarrantees that the clique
833
// in
834
// the tmp_junction_tree that results from the elimination (during the
835
// triangulation process) of the first eliminated node in the separator
836
// between R and S is an admissible candidate
837
for
(
unsigned
int
i
= 0;
i
<
notAffectedneighbourCliques
.
size
(); ++
i
) {
838
// check that the separator is not empty. If this is the case, do not
839
// link the new junction tree to the old one
840
const
NodeSet
&
sep
=
_junction_tree_
.
separator
(
notAffectedneighbourCliques
[
i
]);
841
842
if
(
sep
.
size
() != 0) {
843
// now find the first eliminated node in the separator
844
Size
_elim_order_
=
tmp_graph
.
bound
() + 1;
845
NodeId
elim_node
= 0;
846
847
for
(
const
auto
id
:
sep
) {
848
Size
new_order
=
_triangulation_
->
eliminationOrder
(
id
);
849
850
if
(
new_order
<
_elim_order_
) {
851
_elim_order_
=
new_order
;
852
elim_node
=
id
;
853
}
854
}
855
856
// find the _junction_tree_ clique corresponding to the elimination
857
// of
858
// elim_node and insert an edge between this clique and that which
859
// was
860
// not affected
861
NodeId
&
to_connect
862
=
tmp2global_junction_tree
[
_triangulation_
->
createdJunctionTreeClique
(
elim_node
)];
863
864
NodeId
not_affected
865
=
all_cliques_affected
.
exists
(
notAffectedneighbourCliques
[
i
].
first
())
866
?
notAffectedneighbourCliques
[
i
].
second
()
867
:
notAffectedneighbourCliques
[
i
].
first
();
868
869
_junction_tree_
.
addEdge
(
not_affected
,
to_connect
);
870
871
if
(!
new_nodes_in_junction_tree
.
contains
(
to_connect
)) {
872
_T_mpd_
.
addEdge
(
_mps_of_clique_
[
to_connect
],
_mps_of_clique_
[
not_affected
]);
873
}
874
875
// check that the separator created by the new edge is not equal to
876
// to_connect. If this is the case, then to_connect is included in
877
// not_affected and, hence, should be removed from the graph
878
if
(
_junction_tree_
.
separator
(
not_affected
,
to_connect
).
size
()
879
==
_junction_tree_
.
clique
(
to_connect
).
size
()) {
880
_junction_tree_
.
eraseEdge
(
Edge
(
not_affected
,
to_connect
));
881
882
for
(
const
auto
neighbour
:
_junction_tree_
.
neighbours
(
to_connect
)) {
883
_junction_tree_
.
addEdge
(
neighbour
,
not_affected
);
884
885
if
(!
new_nodes_in_junction_tree
.
contains
(
neighbour
))
886
_T_mpd_
.
addEdge
(
_mps_of_clique_
[
neighbour
],
_mps_of_clique_
[
not_affected
]);
887
}
888
889
_junction_tree_
.
eraseNode
(
to_connect
);
890
891
to_connect
=
not_affected
;
892
}
893
}
894
}
895
}
896
}
897
898
// remove the mps that were affected and update the cliques_of_mps table
899
for
(
const
auto
&
elt
:
all_cliques_affected
) {
900
_mps_of_clique_
.
erase
(
elt
.
first
);
901
_junction_tree_
.
eraseNode
(
elt
.
first
);
902
}
903
904
for
(
const
auto
&
elt
:
_mps_affected_
)
905
if
(
elt
.
second
) {
906
_cliques_of_mps_
.
erase
(
elt
.
first
);
907
_T_mpd_
.
eraseNode
(
elt
.
first
);
908
}
909
}
910
911
/// used for computing the junction tree of the maximal prime subgraphs
912
913
void
IncrementalTriangulation
::
_computeMaxPrimeMergings_
(
914
const
NodeId
node
,
915
const
NodeId
from
,
916
std
::
vector
<
std
::
pair
<
NodeId
,
NodeId
> >&
merged_cliques
,
917
HashTable
<
NodeId
,
bool
>&
mark
,
918
const
NodeSet
&
new_nodes_in_junction_tree
)
const
{
919
mark
[
node
] =
true
;
920
921
// check the separators on all the adjacent edges of Mx
922
for
(
const
auto
other_node
:
_junction_tree_
.
neighbours
(
node
))
923
if
(
other_node
!=
from
) {
924
const
NodeSet
&
separator
=
_junction_tree_
.
separator
(
Edge
(
other_node
,
node
));
925
926
// check that the separator between node and other_node is complete
927
bool
complete
=
true
;
928
929
for
(
auto
iter_sep1
=
separator
.
begin
();
iter_sep1
!=
separator
.
end
() &&
complete
;
930
++
iter_sep1
) {
931
auto
iter_sep2
=
iter_sep1
;
932
933
for
(++
iter_sep2
;
iter_sep2
!=
separator
.
end
(); ++
iter_sep2
) {
934
if
(!
_graph_
.
existsEdge
(*
iter_sep1
, *
iter_sep2
)) {
935
complete
=
false
;
936
break
;
937
}
938
}
939
}
940
941
// here complete indicates whether the separator is complete or not
942
if
(!
complete
)
merged_cliques
.
push_back
(
std
::
pair
<
NodeId
,
NodeId
>(
other_node
,
node
));
943
944
if
(
new_nodes_in_junction_tree
.
contains
(
other_node
))
945
_computeMaxPrimeMergings_
(
other_node
,
946
node
,
947
merged_cliques
,
948
mark
,
949
new_nodes_in_junction_tree
);
950
}
951
}
952
953
/// update the max prime subgraph
954
955
void
956
IncrementalTriangulation
::
_updateMaxPrimeSubgraph_
(
NodeProperty
<
bool
>&
all_cliques_affected
,
957
const
NodeSet
&
new_nodes_in_junction_tree
) {
958
// the maximal prime subgraph join tree is created by aggregation of some
959
// cliques. More precisely, when the separator between 2 cliques is not
960
// complete in the original graph, then the two cliques must be merged.
961
962
// Create a hashtable indicating which clique has been absorbed by some
963
// other
964
// clique. Keys are the cliques absorbed, and values are the cliques that
965
// absorb them.
966
HashTable
<
NodeId
,
NodeId
>
T_mpd_cliques
(
all_cliques_affected
.
size
());
967
968
for
(
const
auto
clik
:
_junction_tree_
.
nodes
())
969
if
(
new_nodes_in_junction_tree
.
contains
(
clik
))
T_mpd_cliques
.
insert
(
clik
,
clik
);
970
971
// parse all the separators of the junction tree and test those that are not
972
// complete in the original graph
973
std
::
vector
<
std
::
pair
<
NodeId
,
NodeId
> >
merged_cliques
;
974
975
HashTable
<
NodeId
,
bool
>
mark
=
T_mpd_cliques
.
map
(
false
);
976
977
for
(
const
auto
&
elt
:
mark
)
978
if
(!
elt
.
second
)
979
_computeMaxPrimeMergings_
(
elt
.
first
,
980
elt
.
first
,
981
merged_cliques
,
982
mark
,
983
new_nodes_in_junction_tree
);
984
985
// compute the transitive closure of merged_cliques. This one will contain
986
// pairs (X,Y) indicating that clique X must be merged with clique Y.
987
// Actually clique X will be inserted into clique Y.
988
for
(
unsigned
int
i
= 0;
i
<
merged_cliques
.
size
(); ++
i
) {
989
if
(
T_mpd_cliques
.
exists
(
merged_cliques
[
i
].
second
))
990
T_mpd_cliques
[
merged_cliques
[
i
].
first
] =
T_mpd_cliques
[
merged_cliques
[
i
].
second
];
991
else
992
T_mpd_cliques
[
merged_cliques
[
i
].
first
] =
_mps_of_clique_
[
merged_cliques
[
i
].
second
];
993
}
994
995
// now we can create the max prime junction tree.
996
997
// create a map translating the cliques' ids in the junction tree into
998
// cliques' id in the T_mpd_ tree
999
NodeProperty
<
NodeId
>
clique2MPS
(
T_mpd_cliques
.
size
());
1000
1001
// First, create the new cliques and create the corresponding
1002
// cliques_of_mps entries
1003
for
(
const
auto
&
elt
:
T_mpd_cliques
)
1004
if
(
elt
.
first
==
elt
.
second
) {
1005
NodeId
newId
=
_T_mpd_
.
addNode
(
_junction_tree_
.
clique
(
elt
.
second
));
1006
clique2MPS
.
insert
(
elt
.
second
,
newId
);
1007
std
::
vector
<
NodeId
>&
vect_of_cliques
1008
=
_cliques_of_mps_
.
insert
(
newId
,
std
::
vector
<
NodeId
>()).
second
;
1009
vect_of_cliques
.
push_back
(
elt
.
second
);
1010
}
1011
1012
// add to the cliques previously created the nodes of the cliques that were
1013
// merged into them and update the cliques_of_mps
1014
for
(
const
auto
&
elt
:
T_mpd_cliques
)
1015
if
((
elt
.
first
!=
elt
.
second
) && (
new_nodes_in_junction_tree
.
contains
(
elt
.
second
))) {
1016
const
NodeId
idMPS
=
clique2MPS
[
elt
.
second
];
1017
1018
for
(
const
auto
node
:
_junction_tree_
.
clique
(
elt
.
first
)) {
1019
try
{
1020
_T_mpd_
.
addToClique
(
idMPS
,
node
);
1021
}
catch
(
DuplicateElement
&) {}
1022
}
1023
1024
_cliques_of_mps_
[
idMPS
].
push_back
(
elt
.
first
);
1025
}
1026
1027
// update the mps_of_node and the mps_of_clique
1028
for
(
const
auto
&
elt
:
T_mpd_cliques
) {
1029
const
NodeId
idMPS
=
clique2MPS
[
elt
.
second
];
1030
_mps_of_clique_
.
insert
(
elt
.
first
,
idMPS
);
1031
1032
if
(
elt
.
first
==
elt
.
second
)
1033
for
(
const
auto
node
:
_T_mpd_
.
clique
(
idMPS
))
1034
_mps_of_node_
[
node
].
insert
(
idMPS
);
1035
}
1036
1037
// add the edges to the max prime subgraph tree
1038
for
(
const
auto
&
elt
:
T_mpd_cliques
) {
1039
NodeId
clique
=
clique2MPS
[
elt
.
second
];
1040
1041
for
(
const
auto
othernode
:
_junction_tree_
.
neighbours
(
elt
.
first
))
1042
if
(
T_mpd_cliques
.
exists
(
othernode
)) {
1043
// here iter is linked to another node that has been created during
1044
// the triangulation
1045
NodeId
otherClique
=
clique2MPS
[
T_mpd_cliques
[
othernode
]];
1046
1047
// avoid adding the same edge several times
1048
if
(
clique
>
otherClique
) {
_T_mpd_
.
addEdge
(
clique
,
otherClique
); }
1049
}
else
{
1050
_T_mpd_
.
addEdge
(
clique
,
_mps_of_clique_
[
othernode
]);
1051
}
1052
}
1053
}
1054
1055
/// updates the triangulated graph using the modif list
1056
1057
void
IncrementalTriangulation
::
updateTriangulation
() {
1058
if
(!
_require_update_
)
return
;
1059
// the set of all the cliques that should be affected by the different
1060
// triangulations we will perform (one by connected component)
1061
NodeProperty
<
bool
>
all_cliques_affected
(
_junction_tree_
.
size
());
1062
1063
// we need to keep track of the new node ids that will be inserted
1064
// into _junction_tree_. A priori, these should be equal to the ids
1065
// inserted into tmp2global_junction_tree. But, sometimes, some new nodes
1066
// are included into old nodes and, in this case, the translation in
1067
// tmp2global_junction_tree indicates the the new node inserted corresponds
1068
// to an old node. Here we wish to know this additional information
1069
NodeSet
new_nodes_in_junction_tree
;
1070
1071
_updateJunctionTree_
(
all_cliques_affected
,
new_nodes_in_junction_tree
);
1072
1073
// now update the T_mpd so that it be coherent with the junction tree
1074
_updateMaxPrimeSubgraph_
(
all_cliques_affected
,
new_nodes_in_junction_tree
);
1075
1076
// reset the MPS that are affected
1077
_mps_affected_
.
clear
();
1078
1079
for
(
const
auto
node
:
_T_mpd_
.
nodes
())
1080
_mps_affected_
.
insert
(
node
,
false
);
1081
1082
// remove all the structures used by the triangulation algorithm
1083
_triangulation_
->
clear
();
1084
1085
_require_update_
=
false
;
1086
}
1087
1088
/// sets the graph to the empty graph
1089
1090
void
IncrementalTriangulation
::
clear
() {
1091
_graph_
.
clear
();
1092
_domain_sizes_
.
clear
();
1093
_junction_tree_
.
clear
();
1094
_T_mpd_
.
clear
();
1095
_mps_of_node_
.
clear
();
1096
_cliques_of_mps_
.
clear
();
1097
_mps_of_clique_
.
clear
();
1098
_mps_affected_
.
clear
();
1099
_triangulation_
->
clear
();
1100
_require_update_
=
false
;
1101
_require_elimination_order_
=
false
;
1102
_elimination_order_
.
clear
();
1103
_reverse_elimination_order_
.
clear
();
1104
_require_created_JT_cliques_
=
false
;
1105
_created_JT_cliques_
.
clear
();
1106
}
1107
1108
/// a collect algorithm to compute, for each node, one container JT's clique
1109
1110
void
IncrementalTriangulation
::
_collectJTCliques_
(
const
NodeId
clique
,
1111
const
NodeId
from
,
1112
NodeProperty
<
bool
>&
examined
) {
1113
// apply collect to all the neighbours except from
1114
for
(
const
auto
otherclique
:
_junction_tree_
.
neighbours
(
clique
))
1115
if
(
otherclique
!=
from
)
_collectJTCliques_
(
otherclique
,
clique
,
examined
);
1116
1117
// get the nodes that belong to clique and not to from
1118
examined
[
clique
] =
true
;
1119
1120
const
NodeSet
&
cliquenodes
=
_junction_tree_
.
clique
(
clique
);
1121
1122
if
(
from
!=
clique
) {
1123
const
NodeSet
&
separator
=
_junction_tree_
.
separator
(
clique
,
from
);
1124
1125
for
(
const
auto
cli
:
cliquenodes
)
1126
if
(!
separator
.
contains
(
cli
))
_created_JT_cliques_
.
insert
(
cli
,
clique
);
1127
}
else
{
1128
for
(
const
auto
cli
:
cliquenodes
)
1129
_created_JT_cliques_
.
insert
(
cli
,
clique
);
1130
}
1131
}
1132
1133
/** @brief returns the Ids of the cliques of the junction tree created by the
1134
* elimination of the nodes */
1135
1136
const
NodeProperty
<
NodeId
>&
IncrementalTriangulation
::
createdJunctionTreeCliques
() {
1137
// check if we already computed the containing cliques
1138
if
(!
_require_created_JT_cliques_
)
return
_created_JT_cliques_
;
1139
1140
// we first we compute the junction tree
1141
updateTriangulation
();
1142
1143
_created_JT_cliques_
.
clear
();
1144
1145
_require_created_JT_cliques_
=
false
;
1146
1147
if
(
_junction_tree_
.
size
() == 0) {
return
_created_JT_cliques_
; }
1148
1149
// now we can use a collect algorithm to get the containing cliques
1150
NodeProperty
<
bool
>
examined
=
_junction_tree_
.
nodesProperty
<
bool
>(
false
);
1151
1152
for
(
const
auto
&
elt
:
examined
)
1153
if
(!
elt
.
second
)
_collectJTCliques_
(
elt
.
first
,
elt
.
first
,
examined
);
1154
1155
return
_created_JT_cliques_
;
1156
}
1157
1158
/// returns a clique containing a given node of the triangulated graph
1159
1160
NodeId
IncrementalTriangulation
::
createdJunctionTreeClique
(
NodeId
id
) {
1161
createdJunctionTreeCliques
();
1162
return
_created_JT_cliques_
[
id
];
1163
}
1164
1165
/** @brief returns the Id of the maximal prime subgraph created by the
1166
* elimination of a given node during the triangulation process */
1167
1168
NodeId
IncrementalTriangulation
::
createdMaxPrimeSubgraph
(
const
NodeId
id
) {
1169
// get the created junction tree clique and get its MPS
1170
return
_mps_of_clique_
[
createdJunctionTreeClique
(
id
)];
1171
}
1172
1173
/// changes the current graph
1174
1175
void
IncrementalTriangulation
::
setGraph
(
const
UndiGraph
*
graph
,
1176
const
NodeProperty
<
Size
>*
dom_sizes
) {
1177
// check that both the graph and the domain sizes are different from nullptr
1178
// or else that both are equal to nullptr
1179
if
(((
graph
!=
nullptr
) && (
dom_sizes
==
nullptr
))
1180
|| ((
graph
==
nullptr
) && (
dom_sizes
!=
nullptr
))) {
1181
GUM_ERROR
(
GraphError
,
1182
"one of the graph or the set of domain sizes "
1183
"is a null pointer."
);
1184
}
1185
1186
// remove the current graph
1187
clear
();
1188
1189
// copy the graph passed in arent and update the structures
1190
// containing the informations useful for the triangulation
1191
if
(
graph
!=
nullptr
) {
1192
for
(
const
auto
node
: *
graph
)
1193
addNode
(
node
, (*
dom_sizes
)[
node
]);
1194
1195
for
(
const
auto
&
edge
:
graph
->
edges
())
1196
addEdge
(
edge
.
first
(),
edge
.
second
());
1197
}
1198
}
1199
1200
/// a collect algorithm to compute elimination orderings
1201
1202
void
IncrementalTriangulation
::
_collectEliminationOrder_
(
const
NodeId
node
,
1203
const
NodeId
from
,
1204
NodeProperty
<
bool
>&
examined
,
1205
Idx
&
index
) {
1206
// apply collect to all the neighbours except from
1207
for
(
const
auto
othernode
:
_junction_tree_
.
neighbours
(
node
))
1208
if
(
othernode
!=
from
)
_collectEliminationOrder_
(
othernode
,
node
,
examined
,
index
);
1209
1210
// get the nodes that belong to node and not to from
1211
examined
[
node
] =
true
;
1212
1213
const
NodeSet
&
clique
=
_junction_tree_
.
clique
(
node
);
1214
1215
if
(
from
!=
node
) {
1216
const
NodeSet
&
separator
=
_junction_tree_
.
separator
(
node
,
from
);
1217
1218
for
(
const
auto
cli
:
clique
) {
1219
if
(!
separator
.
contains
(
cli
)) {
1220
_elimination_order_
[
index
] =
cli
;
1221
_reverse_elimination_order_
.
insert
(
cli
,
index
);
1222
++
index
;
1223
}
1224
}
1225
}
else
{
1226
for
(
const
auto
cli
:
clique
) {
1227
_elimination_order_
[
index
] =
cli
;
1228
_reverse_elimination_order_
.
insert
(
cli
,
index
);
1229
++
index
;
1230
}
1231
}
1232
}
1233
1234
/// get a possible elimination ordering
1235
1236
const
std
::
vector
<
NodeId
>&
IncrementalTriangulation
::
eliminationOrder
() {
1237
// check if we already computed the elimination order
1238
if
(!
_require_elimination_order_
)
return
_elimination_order_
;
1239
1240
// to compute the elimination order, we first we compute the junction tree
1241
updateTriangulation
();
1242
1243
_elimination_order_
.
resize
(
_graph_
.
size
());
1244
1245
_reverse_elimination_order_
.
clear
();
1246
1247
_require_elimination_order_
=
false
;
1248
1249
if
(
_junction_tree_
.
size
() ==
Size
(0)) {
return
_elimination_order_
; }
1250
1251
// now we can use a collect algorithm to get the elimination order
1252
Idx
index
=
Idx
(0);
1253
1254
NodeProperty
<
bool
>
examined
=
_junction_tree_
.
nodesProperty
<
bool
>(
false
);
1255
1256
for
(
const
auto
&
elt
:
examined
)
1257
if
(!
elt
.
second
)
_collectEliminationOrder_
(
elt
.
first
,
elt
.
first
,
examined
,
index
);
1258
1259
return
_elimination_order_
;
1260
}
1261
1262
/** @brief returns the number of a given node in the elimination order
1263
* (0 = first node eliminated) */
1264
1265
Idx
IncrementalTriangulation
::
eliminationOrder
(
const
NodeId
node
) {
1266
if
(!
_graph_
.
existsNode
(
node
)) {
GUM_ERROR
(
NotFound
,
"the node "
<<
node
<<
" does not exist"
) }
1267
1268
// compute the elimination order
1269
eliminationOrder
();
1270
1271
return
_reverse_elimination_order_
[
node
];
1272
}
1273
1274
}
/* namespace gum */
1275
1276
#
endif
/* DOXYGEN_SHOULD_SKIP_THIS */
gum::Set::emplace
INLINE void emplace(Args &&... args)
Definition:
set_tpl.h:643