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