aGrUM  0.14.2
schedule_tpl.h
Go to the documentation of this file.
1 /***************************************************************************
2  * Copyright (C) 2005 by Pierre-Henri WUILLEMIN et Christophe GONZALES *
3  * {prenom.nom}_at_lip6.fr *
4  * *
5  * This program is free software; you can redistribute it and/or modify *
6  * it under the terms of the GNU General Public License as published by *
7  * the Free Software Foundation; either version 2 of the License, or *
8  * (at your option) any later version. *
9  * *
10  * This program is distributed in the hope that it will be useful, *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13  * GNU General Public License for more details. *
14  * *
15  * You should have received a copy of the GNU General Public License *
16  * along with this program; if not, write to the *
17  * Free Software Foundation, Inc., *
18  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19  ***************************************************************************/
25 #ifndef DOXYGEN_SHOULD_SKIP_THIS
26 
27 # include <agrum/agrum.h>
28 
29 namespace gum {
30 
32  template < typename GUM_SCALAR >
34  // for debugging purposes
35  GUM_CONSTRUCTOR(Schedule);
36  }
37 
39  template < typename GUM_SCALAR >
40  Schedule< GUM_SCALAR >::Schedule(const Schedule< GUM_SCALAR >& from) :
41  __dag(from.__dag), __operation2node(from.__operation2node),
42  __created_multidims(from.__created_multidims),
43  __operations_with_wrong_parents(from.__operations_with_wrong_parents),
44  __operations_available(from.__operations_available) {
45  // for debugging purposes
46  GUM_CONS_CPY(Schedule);
47 
48  // we must now copy the operations of "from" into "this"
49  for (const auto& elt : from.__node2operation)
50  __node2operation.insert(elt.first, elt.second->newFactory());
51 
52  // update the set of operations involved with each multidim table
53  for (const auto& elt : from.__multidim2operations)
54  __multidim2operations.insert(elt.first, new NodeSet(*elt.second));
55  }
56 
58  template < typename GUM_SCALAR >
60  // for debugging purposes
61  GUM_DESTRUCTOR(Schedule);
62 
63  // remove all the operations that were stored
64 
65  for (const auto& elt : __node2operation)
66  delete elt.second;
67 
68  // remove the sets of operations involved with each multidim table
69  for (const auto& elt : __multidim2operations)
70  delete elt.second;
71  }
72 
74  template < typename GUM_SCALAR >
75  Schedule< GUM_SCALAR >& Schedule< GUM_SCALAR >::operator=(const Schedule& from) {
76  // avoid self assignment
77  if (this != &from) {
78  // remove all the operations that were stored
79  for (const auto& elt : __node2operation)
80  delete elt.second;
81 
82  // remove the sets of operations involved with each multidim table
83  for (const auto& elt : __multidim2operations)
84  delete elt.second;
85 
86  // fill all the data structures with the elements of from
87  __dag = from.__dag;
88  __operation2node = from.__operation2node;
89  __created_multidims = from.__created_multidims;
90  __operations_with_wrong_parents = from.__operations_with_wrong_parents;
91  __operations_available = from.__operations_available;
92 
93  __node2operation.clear();
94 
95  for (const auto& elt : from.__node2operation)
96  __node2operation.insert(elt.first, elt.second->newFactory());
97 
98  // update the set of operations involved with each multidim table
99  __multidim2operations.clear();
100 
101  for (const auto& elt : from.__multidim2operations)
102  __multidim2operations.insert(elt.first, new NodeSet(*elt.second));
103  }
104 
105  return *this;
106  }
107 
109  template < typename GUM_SCALAR >
110  NodeId
111  Schedule< GUM_SCALAR >::insert(const ScheduleOperation< GUM_SCALAR >& op) {
112  // create a copy of the operation
113  ScheduleOperation< GUM_SCALAR >* operation = op.newFactory();
114 
115  // create a new node for the operation in the DAG
116  NodeId node_id = __dag.addNode();
117 
118  // assign the operation to the new node
119  __node2operation.insert(node_id, operation);
120  __operation2node.insert(operation->id(), node_id);
121 
122  // get the list of multidims passed in arguments and determine which ones
123  // are abstract. If there are some abstract multidims, then the node we
124  // just created must have parents. Try to add these arcs and, if some
125  // parents have not been created yet, indicate it in the
126  // __operations_with_wrong_parents list
127  bool operation_available = true;
128 
129  for (const auto par : operation->multiDimArgs()) {
130  if (par->isAbstract()) {
131  // here we shall have a parent in the graph
132  operation_available = false;
133  MultiDimId multidim_id = par->id();
134 
135  if (__created_multidims.exists(multidim_id)) {
136  __dag.addArc(__created_multidims[multidim_id], node_id);
137  } else {
139  break;
140  }
141  }
142  }
143 
144  // if the operation is available to be processed, mark it as such
145  if (operation_available) __operations_available.insert(node_id);
146 
147  // now we shall find whether, upon executing the operation, new multidim
148  // tables are created
149  NodeSet* involved_ops;
150 
151  for (const auto tab : operation->multiDimResults()) {
152  MultiDimId table_id = tab->id();
153 
154  if (tab->isAbstract()) __created_multidims.insert(table_id, node_id);
155 
156  if (!__multidim2operations.exists(table_id)) {
157  involved_ops = __multidim2operations.insert(table_id, new NodeSet).second;
158  } else {
159  involved_ops = __multidim2operations[table_id];
160  }
161 
162  involved_ops->insert(node_id);
163  }
164 
165  // update __multidim2operations with the arguments passed to the newly
166  // added operation
167  for (const auto& par : operation->multiDimArgs()) {
168  MultiDimId table_id = par->id();
169 
170  if (!__multidim2operations.exists(table_id)) {
171  involved_ops = __multidim2operations.insert(table_id, new NodeSet).second;
172  } else {
173  involved_ops = __multidim2operations[table_id];
174  }
175 
176  involved_ops->insert(node_id);
177  }
178 
179  return node_id;
180  }
181 
184  template < typename GUM_SCALAR >
186  // parse all the nodes whose parents sets are incorrect
187 
188  auto localWrongs =
189  __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 
223  }
224  }
225  }
226 
229  template < typename GUM_SCALAR >
230  INLINE void Schedule< GUM_SCALAR >::forceAfter(NodeId op_to_force,
231  NodeId op_before) {
232  // first, add the constraint into the graph
233  __dag.addArc(op_before, op_to_force);
234 
235  // if op_to_force was available, it is not anymore
236  __operations_available.erase(op_to_force);
237  }
238 
241  template < typename GUM_SCALAR >
243  const ScheduleOperation< GUM_SCALAR >& op_to_force,
244  const ScheduleOperation< GUM_SCALAR >& op_before) {
245  forceAfter(__operation2node[op_to_force.id()],
246  __operation2node[op_before.id()]);
247  }
248 
251  template < typename GUM_SCALAR >
253  const NodeSet& ops_before) {
254  for (const auto op : ops_before)
255  if (op != op_to_force) forceAfter(op_to_force, op);
256  }
257 
260  template < typename GUM_SCALAR >
262  const ScheduleOperation< GUM_SCALAR >& op_to_force,
263  const Set< const ScheduleOperation< GUM_SCALAR >* >& ops_before) {
264  for (const auto op : ops_before)
265  if (*op != op_to_force) forceAfter(op_to_force, *op);
266  }
267 
270  template < typename GUM_SCALAR >
271  INLINE void Schedule< GUM_SCALAR >::forceBefore(NodeId op_to_force,
272  NodeId op_after) {
273  // first, add the constraint into the graph
274  __dag.addArc(op_to_force, op_after);
275 
276  // if op_to_force was available, it is not anymore
277  __operations_available.erase(op_after);
278  }
279 
282  template < typename GUM_SCALAR >
284  const ScheduleOperation< GUM_SCALAR >& op_to_force,
285  const ScheduleOperation< GUM_SCALAR >& op_after) {
286  forceBefore(__operation2node[op_to_force.id()],
287  __operation2node[op_after.id()]);
288  }
289 
292  template < typename GUM_SCALAR >
294  const NodeSet& ops_after) {
295  for (const auto op : ops_after)
296  if (op != op_to_force) forceBefore(op_to_force, op);
297  }
298 
301  template < typename GUM_SCALAR >
303  const ScheduleOperation< GUM_SCALAR >& op_to_force,
304  const Set< const ScheduleOperation< GUM_SCALAR >* >& ops_after) {
305  for (typename Set< const ScheduleOperation< GUM_SCALAR >* >::const_iterator
306  iter = ops_after.begin();
307  iter != ops_after.end();
308  ++iter) {
309  if (**iter != op_to_force) { forceBefore(op_to_force, **iter); }
310  }
311  }
312 
314  template < typename GUM_SCALAR >
316  const ScheduleMultiDim< GUM_SCALAR >& table) const {
317  return *(__multidim2operations[table.id()]);
318  }
319 
321  template < typename GUM_SCALAR >
322  INLINE const NodeSet&
324  return *(__multidim2operations[table_id]);
325  }
326 
328  template < typename GUM_SCALAR >
329  INLINE const DAG& Schedule< GUM_SCALAR >::scheduling_dag() const {
330  // first update the set of parents of the nodes of the graph whose parents
331  // were not set correctly
333  return __dag;
334  }
335 
337  template < typename GUM_SCALAR >
338  INLINE const ScheduleOperation< GUM_SCALAR >&
340  return *(__node2operation[node_id]);
341  }
342 
344  template < typename GUM_SCALAR >
346  const ScheduleOperation< GUM_SCALAR >& op) const {
347  return __operation2node[op.id()];
348  }
349 
351  template < typename GUM_SCALAR >
352  INLINE const NodeProperty< const ScheduleOperation< GUM_SCALAR >* >&
354  return reinterpret_cast<
355  NodeProperty< const ScheduleOperation< GUM_SCALAR >* >& >(__node2operation);
356  }
357 
359  template < typename GUM_SCALAR >
361  // update the set of parents
363  return __operations_available;
364  }
365 
367  template < typename GUM_SCALAR >
369  // update the parents of the sets of nodes which were not correct
370  // !!! it is important to execute the following method before the execution
371  // of operation id: this guarantees that the children of id with exactly
372  // one parent (i.e., id) are now available to be processed
374 
375  // before executing an operation, check that the operation is available
376 
377  if (__dag.parents(id).size() != 0) {
378  GUM_ERROR(OperationNotAllowed, "the operation cannot be executed yet");
379  }
380 
381  // actually execute the operation
382  __node2operation[id]->execute();
383 
384  // now update the availability of the children of id: a child is available
385  // if and only if it has only one parent
386  const NodeSet& children = __dag.children(id);
387 
388  for (const auto child : children)
389  if (__dag.parents(child).size() == 1) __operations_available.insert(child);
390 
391  // remove the operation's node and its adjacent arcs
392  __dag.eraseChildren(id);
393 
394  __dag.eraseNode(id);
395 
396  delete __node2operation[id];
397 
398  __operation2node.erase(__node2operation[id]->id());
399 
400  __node2operation.erase(id);
401 
403  }
404 
406  template < typename GUM_SCALAR >
407  INLINE void
408  Schedule< GUM_SCALAR >::execute(const ScheduleOperation< GUM_SCALAR >& op) {
409  execute(__operation2node[op.id()]);
410  }
411 
414  template < typename GUM_SCALAR >
415  INLINE float Schedule< GUM_SCALAR >::nbOperations(NodeId id) const {
416  return __node2operation[id]->nbOperations();
417  }
418 
421  template < typename GUM_SCALAR >
423  ScheduleOperation< GUM_SCALAR >& op) const {
424  return op.nbOperations();
425  }
426 
428  template < typename GUM_SCALAR >
429  INLINE std::pair< long, long >
431  return __node2operation[id]->memoryUsage();
432  }
433 
435  template < typename GUM_SCALAR >
436  INLINE std::pair< long, long > Schedule< GUM_SCALAR >::memoryUsage(
437  ScheduleOperation< GUM_SCALAR >& op) const {
438  return op.memoryUsage();
439  }
440 
441 } /* namespace gum */
442 
443 #endif /* DOXYGEN_SHOULD_SKIP_THIS */
NodeSet __operations_available
the set of operations that can be executed at once
Definition: schedule.h:228
void execute(NodeId)
executes a given operation (if this one is available)
NodeProperty< ScheduleOperation< GUM_SCALAR > *> __node2operation
a hashtable assigning to each node of the DAG an operation
Definition: schedule.h:208
Set< NodeId > NodeSet
Some typdefs and define for shortcuts ...
void erase(const Key &key)
Removes a given element from the hash table.
DAG __dag
the DAG of the operations to perform
Definition: schedule.h:205
Idx MultiDimId
to identify correctly the ids that correspond to ScheduleMultiDim ids
Definition: schedule.h:71
NodeId nodeId(const ScheduleOperation< GUM_SCALAR > &) const
returns the id of the node corresponding to a given ScheduleOperation
bool exists(const Key &key) const
Checks whether there exists an element with a given key in the hashtable.
NodeSet __operations_with_wrong_parents
a list of operations whose parents are not properly set
Definition: schedule.h:225
void erase(const Key &k)
Erases an element from the set.
Definition: set_tpl.h:653
gum is the global namespace for all aGrUM entities
Definition: agrum.h:25
void forceAfter(const ScheduleOperation< GUM_SCALAR > &op_to_force, const ScheduleOperation< GUM_SCALAR > &op_before)
adds a constraint indicating that an operation cannot be performed before another one ...
virtual NodeId addNode()
insert a new node and return its id
NodeId insert(const ScheduleOperation< GUM_SCALAR > &)
inserts an operation to be scheduled
const NodeSet & operationsInvolving(const ScheduleMultiDim< GUM_SCALAR > &table) const
returns the set of operations involving a given multidim table
const DAG & scheduling_dag() const
returns a DAG indicating in which order the operations can be performed
const NodeSet & availableOperations() const
returns the set of ScheduleOperations that can be executed at once
~Schedule()
destructor
Schedule< GUM_SCALAR > & operator=(const Schedule< GUM_SCALAR > &)
copy operator
const NodeSet & parents(const NodeId id) const
returns the set of nodes with arc ingoing to a given node
virtual void addArc(const NodeId tail, const NodeId head)
insert a new arc into the directed graph
Definition: DAG_inl.h:40
void eraseChildren(const NodeId id)
removes all the children of a given node
std::pair< long, long > memoryUsage(NodeId) const
returns the memory consumption used during the execution of an operation
const NodeSet & children(const NodeId id) const
returns the set of nodes with arc outgoing from a given node
void forceBefore(const ScheduleOperation< GUM_SCALAR > &op_to_force, const ScheduleOperation< GUM_SCALAR > &op_after)
adds a constraint indicating that an operation must be performed before another one ...
const ScheduleOperation< GUM_SCALAR > & operation(NodeId) const
returns the scheduleOperation corresponding to an id in the DAG
Schedule()
default constructor (construct an empty sequence)
void clear()
Removes all the elements, if any, from the set.
Definition: set_tpl.h:372
std::size_t Size
In aGrUM, hashed values are unsigned long int.
Definition: types.h:45
Size size() const noexcept
Returns the number of elements in the set.
Definition: set_tpl.h:698
HashTable< OperationId, NodeId > __operation2node
a hashtable assigning to each operation id a node id in the DAG
Definition: schedule.h:211
value_type & insert(const Key &key, const Val &val)
Adds a new element (actually a copy of this element) into the hash table.
void __updateWrongParents() const
updates the set of parents for the nodes whoses parents are not correct yet and update accordingly th...
HashTable< MultiDimId, NodeId > __created_multidims
a hashtable assigning to each ScheduleMultiDim resulting from a computation the MultiDimOperation nod...
Definition: schedule.h:215
virtual void eraseNode(const NodeId id)
remove a node and its adjacent arcs from the graph
Definition: diGraph_inl.h:66
float nbOperations(NodeId) const
returns an estimation of the number of elementary operations needed to perform a given ScheduleOperat...
Size NodeId
Type for node ids.
Definition: graphElements.h:97
void insert(const Key &k)
Inserts a new element into the set.
Definition: set_tpl.h:610
const NodeProperty< const ScheduleOperation< GUM_SCALAR > *> & operations() const
resturns the association between operations anf nodeIds
#define GUM_ERROR(type, msg)
Definition: exceptions.h:52
HashTable< MultiDimId, NodeSet *> __multidim2operations
for each multidim, store the set of operations involving it
Definition: schedule.h:231