aGrUM
0.20.3
a C++ library for (probabilistic) graphical models
schedule_tpl.h
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 Class containing a schedule of operations to perform on multidims
24
*
25
* @author Christophe GONZALES(@AMU) and Pierre-Henri WUILLEMIN(@LIP6)
26
*/
27
#
ifndef
DOXYGEN_SHOULD_SKIP_THIS
28
29
#
include
<
agrum
/
agrum
.
h
>
30
31
namespace
gum
{
32
33
/// default constructor (construct an empty sequence)
34
template
<
typename
GUM_SCALAR >
35
Schedule< GUM_SCALAR >::Schedule() {
36
// for debugging purposes
37
GUM_CONSTRUCTOR(Schedule);
38
}
39
40
/// copy constructor
41
template
<
typename
GUM_SCALAR
>
42
Schedule
<
GUM_SCALAR
>::
Schedule
(
const
Schedule
<
GUM_SCALAR
>&
from
) :
43
_dag_
(
from
.
_dag_
),
_operation2node_
(
from
.
_operation2node_
),
44
_created_multidims_
(
from
.
_created_multidims_
),
45
_operations_with_wrong_parents_
(
from
.
_operations_with_wrong_parents_
),
46
_operations_available_
(
from
.
_operations_available_
) {
47
// for debugging purposes
48
GUM_CONS_CPY
(
Schedule
);
49
50
// we must now copy the operations of "from" into "this"
51
for
(
const
auto
&
elt
:
from
.
_node2operation_
)
52
_node2operation_
.
insert
(
elt
.
first
,
elt
.
second
->
newFactory
());
53
54
// update the set of operations involved with each multidim table
55
for
(
const
auto
&
elt
:
from
.
_multidim2operations_
)
56
_multidim2operations_
.
insert
(
elt
.
first
,
new
NodeSet
(*
elt
.
second
));
57
}
58
59
/// destructor
60
template
<
typename
GUM_SCALAR
>
61
Schedule
<
GUM_SCALAR
>::~
Schedule
() {
62
// for debugging purposes
63
GUM_DESTRUCTOR
(
Schedule
);
64
65
// remove all the operations that were stored
66
67
for
(
const
auto
&
elt
:
_node2operation_
)
68
delete
elt
.
second
;
69
70
// remove the sets of operations involved with each multidim table
71
for
(
const
auto
&
elt
:
_multidim2operations_
)
72
delete
elt
.
second
;
73
}
74
75
/// copy operator
76
template
<
typename
GUM_SCALAR
>
77
Schedule
<
GUM_SCALAR
>&
Schedule
<
GUM_SCALAR
>::
operator
=(
const
Schedule
&
from
) {
78
// avoid self assignment
79
if
(
this
!= &
from
) {
80
// remove all the operations that were stored
81
for
(
const
auto
&
elt
:
_node2operation_
)
82
delete
elt
.
second
;
83
84
// remove the sets of operations involved with each multidim table
85
for
(
const
auto
&
elt
:
_multidim2operations_
)
86
delete
elt
.
second
;
87
88
// fill all the data structures with the elements of from
89
_dag_
=
from
.
_dag_
;
90
_operation2node_
=
from
.
_operation2node_
;
91
_created_multidims_
=
from
.
_created_multidims_
;
92
_operations_with_wrong_parents_
=
from
.
_operations_with_wrong_parents_
;
93
_operations_available_
=
from
.
_operations_available_
;
94
95
_node2operation_
.
clear
();
96
97
for
(
const
auto
&
elt
:
from
.
_node2operation_
)
98
_node2operation_
.
insert
(
elt
.
first
,
elt
.
second
->
newFactory
());
99
100
// update the set of operations involved with each multidim table
101
_multidim2operations_
.
clear
();
102
103
for
(
const
auto
&
elt
:
from
.
_multidim2operations_
)
104
_multidim2operations_
.
insert
(
elt
.
first
,
new
NodeSet
(*
elt
.
second
));
105
}
106
107
return
*
this
;
108
}
109
110
/// inserts an operation to be scheduled
111
template
<
typename
GUM_SCALAR
>
112
NodeId
Schedule
<
GUM_SCALAR
>::
insert
(
const
ScheduleOperation
<
GUM_SCALAR
>&
op
) {
113
// create a copy of the operation
114
ScheduleOperation
<
GUM_SCALAR
>*
operation
=
op
.
newFactory
();
115
116
// create a new node for the operation in the DAG
117
NodeId
node_id
=
_dag_
.
addNode
();
118
119
// assign the operation to the new node
120
_node2operation_
.
insert
(
node_id
,
operation
);
121
_operation2node_
.
insert
(
operation
->
id
(),
node_id
);
122
123
// get the list of multidims passed in arguments and determine which ones
124
// are abstract. If there are some abstract multidims, then the node we
125
// just created must have parents. Try to add these arcs and, if some
126
// parents have not been created yet, indicate it in the
127
// _operations_with_wrong_parents_ list
128
bool
operation_available
=
true
;
129
130
for
(
const
auto
par
:
operation
->
multiDimArgs
()) {
131
if
(
par
->
isAbstract
()) {
132
// here we shall have a parent in the graph
133
operation_available
=
false
;
134
MultiDimId
multidim_id
=
par
->
id
();
135
136
if
(
_created_multidims_
.
exists
(
multidim_id
)) {
137
_dag_
.
addArc
(
_created_multidims_
[
multidim_id
],
node_id
);
138
}
else
{
139
_operations_with_wrong_parents_
.
insert
(
node_id
);
140
break
;
141
}
142
}
143
}
144
145
// if the operation is available to be processed, mark it as such
146
if
(
operation_available
)
_operations_available_
.
insert
(
node_id
);
147
148
// now we shall find whether, upon executing the operation, new multidim
149
// tables are created
150
NodeSet
*
involved_ops
;
151
152
for
(
const
auto
tab
:
operation
->
multiDimResults
()) {
153
MultiDimId
table_id
=
tab
->
id
();
154
155
if
(
tab
->
isAbstract
())
_created_multidims_
.
insert
(
table_id
,
node_id
);
156
157
if
(!
_multidim2operations_
.
exists
(
table_id
)) {
158
involved_ops
=
_multidim2operations_
.
insert
(
table_id
,
new
NodeSet
).
second
;
159
}
else
{
160
involved_ops
=
_multidim2operations_
[
table_id
];
161
}
162
163
involved_ops
->
insert
(
node_id
);
164
}
165
166
// update _multidim2operations_ with the arguments passed to the newly
167
// added operation
168
for
(
const
auto
&
par
:
operation
->
multiDimArgs
()) {
169
MultiDimId
table_id
=
par
->
id
();
170
171
if
(!
_multidim2operations_
.
exists
(
table_id
)) {
172
involved_ops
=
_multidim2operations_
.
insert
(
table_id
,
new
NodeSet
).
second
;
173
}
else
{
174
involved_ops
=
_multidim2operations_
[
table_id
];
175
}
176
177
involved_ops
->
insert
(
node_id
);
178
}
179
180
return
node_id
;
181
}
182
183
/// updates the set of parents for the nodes whoses parents are not correct
184
/// yet
185
template
<
typename
GUM_SCALAR
>
186
void
Schedule
<
GUM_SCALAR
>::
_updateWrongParents_
()
const
{
187
// parse all the nodes whose parents sets are incorrect
188
189
auto
localWrongs
=
_operations_with_wrong_parents_
;
// complete copy of NodeSet
190
191
for
(
const
auto
wrong
:
localWrongs
) {
192
// get the arguments passed to wrong and check that those that are
193
// abstract
194
// multidims belong to the schedule
195
const
Sequence
<
const
ScheduleMultiDim
<
GUM_SCALAR
>* >&
args
196
=
_node2operation_
[
wrong
]->
multiDimArgs
();
197
bool
still_wrong
=
false
;
198
199
for
(
const
auto
arg
:
args
) {
200
if
(
arg
->
isAbstract
() && !
_created_multidims_
.
exists
(
arg
->
id
())) {
201
still_wrong
=
true
;
202
break
;
203
}
204
}
205
206
// if the operation is not "still_wrong" anymore, then we should remove
207
// it from _operations_with_wrong_parents_ and update its parents set
208
// appropriately. In addition, if there is no parent, then we should
209
// indicate that the operation is now available
210
if
(!
still_wrong
) {
211
Size
nb_parents
= 0;
212
213
for
(
const
auto
arg
:
args
)
214
if
(
arg
->
isAbstract
()) {
215
_dag_
.
addArc
(
_created_multidims_
[
arg
->
id
()],
wrong
);
216
++
nb_parents
;
217
}
218
219
// check that there is no parent
220
if
(!
nb_parents
) {
_operations_available_
.
insert
(
wrong
); }
221
222
_operations_with_wrong_parents_
.
erase
(
wrong
);
223
}
224
}
225
}
226
227
/** @brief adds a constraint indicating that an operation cannot be performed
228
* before another one */
229
template
<
typename
GUM_SCALAR
>
230
INLINE
void
Schedule
<
GUM_SCALAR
>::
forceAfter
(
NodeId
op_to_force
,
NodeId
op_before
) {
231
// first, add the constraint into the graph
232
_dag_
.
addArc
(
op_before
,
op_to_force
);
233
234
// if op_to_force was available, it is not anymore
235
_operations_available_
.
erase
(
op_to_force
);
236
}
237
238
/** @brief adds a constraint indicating that an operation cannot be performed
239
* before another one */
240
template
<
typename
GUM_SCALAR
>
241
INLINE
void
Schedule
<
GUM_SCALAR
>::
forceAfter
(
const
ScheduleOperation
<
GUM_SCALAR
>&
op_to_force
,
242
const
ScheduleOperation
<
GUM_SCALAR
>&
op_before
) {
243
forceAfter
(
_operation2node_
[
op_to_force
.
id
()],
_operation2node_
[
op_before
.
id
()]);
244
}
245
246
/** @brief adds a constraint indicating that an operation cannot be performed
247
* before a set of operations */
248
template
<
typename
GUM_SCALAR
>
249
void
Schedule
<
GUM_SCALAR
>::
forceAfter
(
NodeId
op_to_force
,
const
NodeSet
&
ops_before
) {
250
for
(
const
auto
op
:
ops_before
)
251
if
(
op
!=
op_to_force
)
forceAfter
(
op_to_force
,
op
);
252
}
253
254
/** @brief adds a constraint indicating that an operation cannot be performed
255
* before a set of operations */
256
template
<
typename
GUM_SCALAR
>
257
void
Schedule
<
GUM_SCALAR
>::
forceAfter
(
258
const
ScheduleOperation
<
GUM_SCALAR
>&
op_to_force
,
259
const
Set
<
const
ScheduleOperation
<
GUM_SCALAR
>* >&
ops_before
) {
260
for
(
const
auto
op
:
ops_before
)
261
if
(*
op
!=
op_to_force
)
forceAfter
(
op_to_force
, *
op
);
262
}
263
264
/** @brief adds a constraint indicating that an operation must be performed
265
* before another one */
266
template
<
typename
GUM_SCALAR
>
267
INLINE
void
Schedule
<
GUM_SCALAR
>::
forceBefore
(
NodeId
op_to_force
,
NodeId
op_after
) {
268
// first, add the constraint into the graph
269
_dag_
.
addArc
(
op_to_force
,
op_after
);
270
271
// if op_to_force was available, it is not anymore
272
_operations_available_
.
erase
(
op_after
);
273
}
274
275
/** @brief adds a constraint indicating that an operation must be performed
276
* before another one */
277
template
<
typename
GUM_SCALAR
>
278
INLINE
void
279
Schedule
<
GUM_SCALAR
>::
forceBefore
(
const
ScheduleOperation
<
GUM_SCALAR
>&
op_to_force
,
280
const
ScheduleOperation
<
GUM_SCALAR
>&
op_after
) {
281
forceBefore
(
_operation2node_
[
op_to_force
.
id
()],
_operation2node_
[
op_after
.
id
()]);
282
}
283
284
/** @brief adds a constraint indicating that an operation must be performed
285
* before a set of operations */
286
template
<
typename
GUM_SCALAR
>
287
void
Schedule
<
GUM_SCALAR
>::
forceBefore
(
NodeId
op_to_force
,
const
NodeSet
&
ops_after
) {
288
for
(
const
auto
op
:
ops_after
)
289
if
(
op
!=
op_to_force
)
forceBefore
(
op_to_force
,
op
);
290
}
291
292
/** @brief adds a constraint indicating that an operation must be performed
293
* before a set of operations */
294
template
<
typename
GUM_SCALAR
>
295
void
Schedule
<
GUM_SCALAR
>::
forceBefore
(
296
const
ScheduleOperation
<
GUM_SCALAR
>&
op_to_force
,
297
const
Set
<
const
ScheduleOperation
<
GUM_SCALAR
>* >&
ops_after
) {
298
for
(
typename
Set
<
const
ScheduleOperation
<
GUM_SCALAR
>* >::
const_iterator
iter
299
=
ops_after
.
begin
();
300
iter
!=
ops_after
.
end
();
301
++
iter
) {
302
if
(**
iter
!=
op_to_force
) {
forceBefore
(
op_to_force
, **
iter
); }
303
}
304
}
305
306
/// returns the set of operations involving a given multidim table
307
template
<
typename
GUM_SCALAR
>
308
INLINE
const
NodeSet
&
Schedule
<
GUM_SCALAR
>::
operationsInvolving
(
309
const
ScheduleMultiDim
<
GUM_SCALAR
>&
table
)
const
{
310
return
*(
_multidim2operations_
[
table
.
id
()]);
311
}
312
313
/// returns the set of operations involving a given multidim table
314
template
<
typename
GUM_SCALAR
>
315
INLINE
const
NodeSet
&
Schedule
<
GUM_SCALAR
>::
operationsInvolving
(
MultiDimId
table_id
)
const
{
316
return
*(
_multidim2operations_
[
table_id
]);
317
}
318
319
/// returns a DAG indicating in which order the operations can be performed
320
template
<
typename
GUM_SCALAR
>
321
INLINE
const
DAG
&
Schedule
<
GUM_SCALAR
>::
scheduling_dag
()
const
{
322
// first update the set of parents of the nodes of the graph whose parents
323
// were not set correctly
324
_updateWrongParents_
();
325
return
_dag_
;
326
}
327
328
/// returns the scheduleOperation corresponding to an id in the DAG
329
template
<
typename
GUM_SCALAR
>
330
INLINE
const
ScheduleOperation
<
GUM_SCALAR
>&
331
Schedule
<
GUM_SCALAR
>::
operation
(
NodeId
node_id
)
const
{
332
return
*(
_node2operation_
[
node_id
]);
333
}
334
335
/// returns the id associated to a given operation
336
template
<
typename
GUM_SCALAR
>
337
INLINE
NodeId
Schedule
<
GUM_SCALAR
>::
nodeId
(
const
ScheduleOperation
<
GUM_SCALAR
>&
op
)
const
{
338
return
_operation2node_
[
op
.
id
()];
339
}
340
341
/// returns the association between operations anf nodeIds
342
template
<
typename
GUM_SCALAR
>
343
INLINE
const
NodeProperty
<
const
ScheduleOperation
<
GUM_SCALAR
>* >&
344
Schedule
<
GUM_SCALAR
>::
operations
()
const
{
345
return
reinterpret_cast
<
NodeProperty
<
const
ScheduleOperation
<
GUM_SCALAR
>* >& >(
346
_node2operation_
);
347
}
348
349
/// returns the set of ScheduleOperations that can be executed at once
350
template
<
typename
GUM_SCALAR
>
351
INLINE
const
NodeSet
&
Schedule
<
GUM_SCALAR
>::
availableOperations
()
const
{
352
// update the set of parents
353
_updateWrongParents_
();
354
return
_operations_available_
;
355
}
356
357
/// executes a given operation
358
template
<
typename
GUM_SCALAR
>
359
void
Schedule
<
GUM_SCALAR
>::
execute
(
NodeId
id
) {
360
// update the parents of the sets of nodes which were not correct
361
// !!! it is important to execute the following method before the execution
362
// of operation id: this guarantees that the children of id with exactly
363
// one parent (i.e., id) are now available to be processed
364
_updateWrongParents_
();
365
366
// before executing an operation, check that the operation is available
367
368
if
(
_dag_
.
parents
(
id
).
size
() != 0) {
369
GUM_ERROR
(
OperationNotAllowed
,
"the operation cannot be executed yet"
)
370
}
371
372
// actually execute the operation
373
_node2operation_
[
id
]->
execute
();
374
375
// now update the availability of the children of id: a child is available
376
// if and only if it has only one parent
377
const
NodeSet
&
children
=
_dag_
.
children
(
id
);
378
379
for
(
const
auto
child
:
children
)
380
if
(
_dag_
.
parents
(
child
).
size
() == 1)
_operations_available_
.
insert
(
child
);
381
382
// remove the operation's node and its adjacent arcs
383
_dag_
.
eraseChildren
(
id
);
384
385
_dag_
.
eraseNode
(
id
);
386
387
delete
_node2operation_
[
id
];
388
389
_operation2node_
.
erase
(
_node2operation_
[
id
]->
id
());
390
391
_node2operation_
.
erase
(
id
);
392
393
_operations_available_
.
erase
(
id
);
394
}
395
396
/// executes a given operation
397
template
<
typename
GUM_SCALAR
>
398
INLINE
void
Schedule
<
GUM_SCALAR
>::
execute
(
const
ScheduleOperation
<
GUM_SCALAR
>&
op
) {
399
execute
(
_operation2node_
[
op
.
id
()]);
400
}
401
402
/** @bried returns an estimation of the number of elementary operations needed
403
* to perform a given ScheduleOperation */
404
template
<
typename
GUM_SCALAR
>
405
INLINE
float
Schedule
<
GUM_SCALAR
>::
nbOperations
(
NodeId
id
)
const
{
406
return
_node2operation_
[
id
]->
nbOperations
();
407
}
408
409
/** @bried returns an estimation of the number of elementary operations needed
410
* to perform a given ScheduleOperation */
411
template
<
typename
GUM_SCALAR
>
412
INLINE
float
Schedule
<
GUM_SCALAR
>::
nbOperations
(
ScheduleOperation
<
GUM_SCALAR
>&
op
)
const
{
413
return
op
.
nbOperations
();
414
}
415
416
/// returns the memory consumption used during the execution of an operation
417
template
<
typename
GUM_SCALAR
>
418
INLINE
std
::
pair
<
long
,
long
>
Schedule
<
GUM_SCALAR
>::
memoryUsage
(
NodeId
id
)
const
{
419
return
_node2operation_
[
id
]->
memoryUsage
();
420
}
421
422
/// returns the memory consumption used during the execution of an operation
423
template
<
typename
GUM_SCALAR
>
424
INLINE
std
::
pair
<
long
,
long
>
425
Schedule
<
GUM_SCALAR
>::
memoryUsage
(
ScheduleOperation
<
GUM_SCALAR
>&
op
)
const
{
426
return
op
.
memoryUsage
();
427
}
428
429
}
/* namespace gum */
430
431
#
endif
/* DOXYGEN_SHOULD_SKIP_THIS */
gum::Set::emplace
INLINE void emplace(Args &&... args)
Definition:
set_tpl.h:643