Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Rename file .h -> .hpp.
[simgrid.git] / src / surf / maxmin.cpp
1 /* Copyright (c) 2004-2017. The SimGrid Team. All rights reserved.          */
2
3 /* This program is free software; you can redistribute it and/or modify it
4  * under the terms of the license (GNU LGPL) which comes with this package. */
5
6 /* \file callbacks.h */
7
8 #include "surf/maxmin.hpp"
9 #include "xbt/backtrace.hpp"
10 #include "xbt/log.h"
11 #include "xbt/mallocator.h"
12 #include "xbt/sysdep.h"
13 #include <algorithm>
14 #include <cmath>
15 #include <cstdlib>
16 #include <cxxabi.h>
17 #include <limits>
18 #include <vector>
19
20 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(surf_maxmin, surf, "Logging specific to SURF (maxmin)");
21
22 typedef std::vector<int> dyn_light_t;
23
24 double sg_maxmin_precision = 0.00001; /* Change this with --cfg=maxmin/precision:VALUE */
25 double sg_surf_precision   = 0.00001; /* Change this with --cfg=surf/precision:VALUE */
26 int sg_concurrency_limit   = -1;      /* Change this with --cfg=maxmin/concurrency-limit:VALUE */
27
28 int s_lmm_variable_t::Global_debug_id   = 1;
29 int s_lmm_constraint_t::Global_debug_id = 1;
30
31 int s_lmm_element_t::get_concurrency() const
32 {
33   //Ignore element with weight less than one (e.g. cross-traffic)
34   return (consumption_weight >= 1) ? 1 : 0;
35   //There are other alternatives, but they will change the behaviour of the model..
36   //So do not use it unless you want to make a new model.
37   //If you do, remember to change the variables concurrency share to reflect it.
38   //Potential examples are:
39   //return (elem->weight>0)?1:0;//Include element as soon  as weight is non-zero
40   //return (int)ceil(elem->weight);//Include element as the rounded-up integer value of the element weight
41 }
42
43 void s_lmm_element_t::decrease_concurrency()
44 {
45   xbt_assert(constraint->concurrency_current >= get_concurrency());
46   constraint->concurrency_current -= get_concurrency();
47 }
48
49 void s_lmm_element_t::increase_concurrency()
50 {
51   constraint->concurrency_current += get_concurrency();
52
53   if (constraint->concurrency_current > constraint->concurrency_maximum)
54     constraint->concurrency_maximum = constraint->concurrency_current;
55
56   xbt_assert(constraint->get_concurrency_limit() < 0 ||
57                  constraint->concurrency_current <= constraint->get_concurrency_limit(),
58              "Concurrency limit overflow!");
59 }
60
61 void s_lmm_system_t::check_concurrency()
62 {
63   // These checks are very expensive, so do them only if we want to debug SURF LMM
64   if (not XBT_LOG_ISENABLED(surf_maxmin, xbt_log_priority_debug))
65     return;
66
67   void* cnstIt;
68   xbt_swag_foreach(cnstIt, &constraint_set)
69   {
70     lmm_constraint_t cnst = (lmm_constraint_t)cnstIt;
71     int concurrency       = 0;
72     void* elemIt;
73     xbt_swag_foreach(elemIt, &(cnst->enabled_element_set))
74     {
75       lmm_element_t elem = (lmm_element_t)elemIt;
76       xbt_assert(elem->variable->sharing_weight > 0);
77       concurrency += elem->get_concurrency();
78     }
79
80     xbt_swag_foreach(elemIt, &(cnst->disabled_element_set))
81     {
82       lmm_element_t elem = (lmm_element_t)elemIt;
83       // We should have staged variables only if concurrency is reached in some constraint
84       xbt_assert(cnst->get_concurrency_limit() < 0 || elem->variable->staged_weight == 0 ||
85                      elem->variable->get_min_concurrency_slack() < elem->variable->concurrency_share,
86                  "should not have staged variable!");
87     }
88
89     xbt_assert(cnst->get_concurrency_limit() < 0 || cnst->get_concurrency_limit() >= concurrency,
90                "concurrency check failed!");
91     xbt_assert(cnst->concurrency_current == concurrency, "concurrency_current is out-of-date!");
92   }
93
94   // Check that for each variable, all corresponding elements are in the same state (i.e. same element sets)
95   void* varIt;
96   xbt_swag_foreach(varIt, &variable_set)
97   {
98     lmm_variable_t var = (lmm_variable_t)varIt;
99
100     if (var->cnsts.empty())
101       continue;
102
103     lmm_element_t elem     = &var->cnsts[0];
104     int belong_to_enabled  = xbt_swag_belongs(elem, &(elem->constraint->enabled_element_set));
105     int belong_to_disabled = xbt_swag_belongs(elem, &(elem->constraint->disabled_element_set));
106     int belong_to_active   = xbt_swag_belongs(elem, &(elem->constraint->active_element_set));
107
108     for (s_lmm_element_t const& elem : var->cnsts) {
109       xbt_assert(belong_to_enabled == xbt_swag_belongs(&elem, &(elem.constraint->enabled_element_set)),
110                  "Variable inconsistency (1): enabled_element_set");
111       xbt_assert(belong_to_disabled == xbt_swag_belongs(&elem, &(elem.constraint->disabled_element_set)),
112                  "Variable inconsistency (2): disabled_element_set");
113       xbt_assert(belong_to_active == xbt_swag_belongs(&elem, &(elem.constraint->active_element_set)),
114                  "Variable inconsistency (3): active_element_set");
115     }
116   }
117 }
118
119 void s_lmm_system_t::var_free(lmm_variable_t var)
120 {
121   XBT_IN("(sys=%p, var=%p)", this, var);
122   modified = true;
123
124   // TODOLATER Can do better than that by leaving only the variable in only one enabled_element_set, call
125   // update_modified_set, and then remove it..
126   if (not var->cnsts.empty())
127     update_modified_set(var->cnsts[0].constraint);
128
129   for (s_lmm_element_t& elem : var->cnsts) {
130     if (var->sharing_weight > 0)
131       elem.decrease_concurrency();
132     xbt_swag_remove(&elem, &(elem.constraint->enabled_element_set));
133     xbt_swag_remove(&elem, &(elem.constraint->disabled_element_set));
134     xbt_swag_remove(&elem, &(elem.constraint->active_element_set));
135     int nelements = xbt_swag_size(&(elem.constraint->enabled_element_set)) +
136                     xbt_swag_size(&(elem.constraint->disabled_element_set));
137     if (nelements == 0)
138       make_constraint_inactive(elem.constraint);
139     else
140       on_disabled_var(elem.constraint);
141   }
142
143   var->cnsts.clear();
144
145   check_concurrency();
146
147   xbt_mallocator_release(variable_mallocator, var);
148   XBT_OUT();
149 }
150
151 s_lmm_system_t::s_lmm_system_t(bool selective_update) : selective_update_active(selective_update)
152 {
153   s_lmm_variable_t var;
154   s_lmm_constraint_t cnst;
155
156   modified                = false;
157   visited_counter         = 1;
158
159   XBT_DEBUG("Setting selective_update_active flag to %d", selective_update_active);
160
161   xbt_swag_init(&variable_set, xbt_swag_offset(var, variable_set_hookup));
162   xbt_swag_init(&constraint_set, xbt_swag_offset(cnst, constraint_set_hookup));
163
164   xbt_swag_init(&active_constraint_set, xbt_swag_offset(cnst, active_constraint_set_hookup));
165
166   xbt_swag_init(&modified_constraint_set, xbt_swag_offset(cnst, modified_constraint_set_hookup));
167   xbt_swag_init(&saturated_variable_set, xbt_swag_offset(var, saturated_variable_set_hookup));
168   xbt_swag_init(&saturated_constraint_set, xbt_swag_offset(cnst, saturated_constraint_set_hookup));
169
170   keep_track          = nullptr;
171   variable_mallocator = xbt_mallocator_new(65536, s_lmm_system_t::variable_mallocator_new_f,
172                                            s_lmm_system_t::variable_mallocator_free_f, nullptr);
173   solve_fun = &lmm_solve;
174 }
175
176 s_lmm_system_t::~s_lmm_system_t()
177 {
178   lmm_variable_t var;
179   lmm_constraint_t cnst;
180
181   while ((var = extract_variable())) {
182     auto demangled = simgrid::xbt::demangle(typeid(*var->id).name());
183     XBT_WARN("Probable bug: a %s variable (#%d) not removed before the LMM system destruction.", demangled.get(),
184              var->id_int);
185     var_free(var);
186   }
187   while ((cnst = extract_constraint()))
188     cnst_free(cnst);
189
190   xbt_mallocator_free(variable_mallocator);
191 }
192
193 void s_lmm_system_t::cnst_free(lmm_constraint_t cnst)
194 {
195   make_constraint_inactive(cnst);
196   delete cnst;
197 }
198
199 s_lmm_constraint_t::s_lmm_constraint_t(void* id_value, double bound_value) : bound(bound_value), id(id_value)
200 {
201   s_lmm_element_t elem;
202
203   id_int = Global_debug_id++;
204   xbt_swag_init(&enabled_element_set, xbt_swag_offset(elem, enabled_element_set_hookup));
205   xbt_swag_init(&disabled_element_set, xbt_swag_offset(elem, disabled_element_set_hookup));
206   xbt_swag_init(&active_element_set, xbt_swag_offset(elem, active_element_set_hookup));
207
208   remaining           = 0.0;
209   usage               = 0.0;
210   concurrency_limit   = sg_concurrency_limit;
211   concurrency_current = 0;
212   concurrency_maximum = 0;
213   sharing_policy      = 1; /* FIXME: don't hardcode the value */
214
215   lambda     = 0.0;
216   new_lambda = 0.0;
217   cnst_light = nullptr;
218 }
219
220 lmm_constraint_t s_lmm_system_t::constraint_new(void* id, double bound_value)
221 {
222   lmm_constraint_t cnst = new s_lmm_constraint_t(id, bound_value);
223   insert_constraint(cnst);
224   return cnst;
225 }
226
227 void* s_lmm_system_t::variable_mallocator_new_f()
228 {
229   return new s_lmm_variable_t;
230 }
231
232 void s_lmm_system_t::variable_mallocator_free_f(void* var)
233 {
234   delete static_cast<lmm_variable_t>(var);
235 }
236
237 lmm_variable_t s_lmm_system_t::variable_new(simgrid::surf::Action* id, double sharing_weight, double bound,
238                                             int number_of_constraints)
239 {
240   XBT_IN("(sys=%p, id=%p, weight=%f, bound=%f, num_cons =%d)", this, id, sharing_weight, bound, number_of_constraints);
241
242   lmm_variable_t var = static_cast<lmm_variable_t>(xbt_mallocator_get(variable_mallocator));
243   var->initialize(id, sharing_weight, bound, number_of_constraints, visited_counter - 1);
244   if (sharing_weight)
245     xbt_swag_insert_at_head(var, &variable_set);
246   else
247     xbt_swag_insert_at_tail(var, &variable_set);
248
249   XBT_OUT(" returns %p", var);
250   return var;
251 }
252
253 void s_lmm_system_t::variable_free(lmm_variable_t var)
254 {
255   remove_variable(var);
256   var_free(var);
257 }
258
259 void s_lmm_system_t::expand(lmm_constraint_t cnst, lmm_variable_t var, double consumption_weight)
260 {
261   modified = true;
262
263   //Check if this variable already has an active element in this constraint
264   //If it does, substract it from the required slack
265   int current_share = 0;
266   if(var->concurrency_share>1){
267     for (s_lmm_element_t& elem : var->cnsts) {
268       if (elem.constraint == cnst && xbt_swag_belongs(&elem, &(elem.constraint->enabled_element_set)))
269         current_share += elem.get_concurrency();
270     }
271   }
272
273   //Check if we need to disable the variable
274   if (var->sharing_weight > 0 && var->concurrency_share - current_share > cnst->get_concurrency_slack()) {
275     double weight = var->sharing_weight;
276     disable_var(var);
277     for (s_lmm_element_t const& elem : var->cnsts)
278       on_disabled_var(elem.constraint);
279     consumption_weight = 0;
280     var->staged_weight=weight;
281     xbt_assert(not var->sharing_weight);
282   }
283
284   xbt_assert(var->cnsts.size() < var->cnsts.capacity(), "Too much constraints");
285
286   var->cnsts.resize(var->cnsts.size() + 1);
287   s_lmm_element_t& elem = var->cnsts.back();
288
289   elem.consumption_weight = consumption_weight;
290   elem.constraint         = cnst;
291   elem.variable           = var;
292
293   if (var->sharing_weight) {
294     xbt_swag_insert_at_head(&elem, &(elem.constraint->enabled_element_set));
295     elem.increase_concurrency();
296   } else
297     xbt_swag_insert_at_tail(&elem, &(elem.constraint->disabled_element_set));
298
299   if (not selective_update_active) {
300     make_constraint_active(cnst);
301   } else if (elem.consumption_weight > 0 || var->sharing_weight > 0) {
302     make_constraint_active(cnst);
303     update_modified_set(cnst);
304     //TODOLATER: Why do we need this second call?
305     if (var->cnsts.size() > 1)
306       update_modified_set(var->cnsts[0].constraint);
307   }
308
309   check_concurrency();
310 }
311
312 void s_lmm_system_t::expand_add(lmm_constraint_t cnst, lmm_variable_t var, double value)
313 {
314   modified = true;
315
316   check_concurrency();
317
318   //BEWARE: In case you have multiple elements in one constraint, this will always add value to the first element.
319   auto elem_it = std::find_if(begin(var->cnsts), end(var->cnsts),
320                               [&cnst](s_lmm_element_t const& x) { return x.constraint == cnst; });
321   if (elem_it != end(var->cnsts)) {
322     s_lmm_element_t& elem = *elem_it;
323     if (var->sharing_weight)
324       elem.decrease_concurrency();
325
326     if (cnst->sharing_policy)
327       elem.consumption_weight += value;
328     else
329       elem.consumption_weight = std::max(elem.consumption_weight, value);
330
331     //We need to check that increasing value of the element does not cross the concurrency limit
332     if (var->sharing_weight) {
333       if (cnst->get_concurrency_slack() < elem.get_concurrency()) {
334         double weight = var->sharing_weight;
335         disable_var(var);
336         for (s_lmm_element_t const& elem2 : var->cnsts)
337           on_disabled_var(elem2.constraint);
338         var->staged_weight=weight;
339         xbt_assert(not var->sharing_weight);
340       }
341       elem.increase_concurrency();
342     }
343     update_modified_set(cnst);
344   } else
345     expand(cnst, var, value);
346
347   check_concurrency();
348 }
349
350 lmm_variable_t s_lmm_constraint_t::get_variable(lmm_element_t* elem) const
351 {
352   if (*elem == nullptr) {
353     // That is the first call, pick the first element among enabled_element_set (or disabled_element_set if
354     // enabled_element_set is empty)
355     *elem = (lmm_element_t)xbt_swag_getFirst(&enabled_element_set);
356     if (*elem == nullptr)
357       *elem = (lmm_element_t)xbt_swag_getFirst(&disabled_element_set);
358   } else {
359     //elem is not null, so we carry on
360     if (xbt_swag_belongs(*elem, &enabled_element_set)) {
361       //Look at enabled_element_set, and jump to disabled_element_set when finished
362       *elem = (lmm_element_t)xbt_swag_getNext(*elem, enabled_element_set.offset);
363       if (*elem == nullptr)
364         *elem = (lmm_element_t)xbt_swag_getFirst(&disabled_element_set);
365     } else {
366       *elem = (lmm_element_t)xbt_swag_getNext(*elem, disabled_element_set.offset);
367     }
368   }
369   if (*elem)
370     return (*elem)->variable;
371   else
372     return nullptr;
373 }
374
375 //if we modify the swag between calls, normal version may loop forever
376 //this safe version ensures that we browse the swag elements only once
377 lmm_variable_t s_lmm_constraint_t::get_variable_safe(lmm_element_t* elem, lmm_element_t* nextelem, int* numelem) const
378 {
379   if (*elem == nullptr) {
380     *elem    = (lmm_element_t)xbt_swag_getFirst(&enabled_element_set);
381     *numelem = xbt_swag_size(&enabled_element_set) + xbt_swag_size(&disabled_element_set) - 1;
382     if (*elem == nullptr)
383       *elem = (lmm_element_t)xbt_swag_getFirst(&disabled_element_set);
384   }else{
385     *elem = *nextelem;
386     if(*numelem>0){
387      (*numelem) --;
388     }else
389       return nullptr;
390   }
391   if (*elem){
392     //elem is not null, so we carry on
393     if (xbt_swag_belongs(*elem, &enabled_element_set)) {
394       //Look at enabled_element_set, and jump to disabled_element_set when finished
395       *nextelem = (lmm_element_t)xbt_swag_getNext(*elem, enabled_element_set.offset);
396       if (*nextelem == nullptr)
397         *nextelem = (lmm_element_t)xbt_swag_getFirst(&disabled_element_set);
398     } else {
399       *nextelem = (lmm_element_t)xbt_swag_getNext(*elem, disabled_element_set.offset);
400     }
401     return (*elem)->variable;
402   }else
403     return nullptr;
404 }
405
406 static inline void saturated_constraints_update(double usage, int cnst_light_num, dyn_light_t& saturated_constraints,
407                                                 double* min_usage)
408 {
409   xbt_assert(usage > 0,"Impossible");
410
411   if (*min_usage < 0 || *min_usage > usage) {
412     *min_usage = usage;
413     XBT_HERE(" min_usage=%f (cnst->remaining / cnst->usage =%f)", *min_usage, usage);
414     saturated_constraints.assign(1, cnst_light_num);
415   } else if (*min_usage == usage) {
416     saturated_constraints.emplace_back(cnst_light_num);
417   }
418 }
419
420 static inline void saturated_variable_set_update(s_lmm_constraint_light_t* cnst_light_tab,
421                                                  const dyn_light_t& saturated_constraints, lmm_system_t sys)
422 {
423   /* Add active variables (i.e. variables that need to be set) from the set of constraints to saturate (cnst_light_tab)*/
424   for (int const& saturated_cnst : saturated_constraints) {
425     lmm_constraint_light_t cnst = &cnst_light_tab[saturated_cnst];
426     void* _elem;
427     xbt_swag_t elem_list = &(cnst->cnst->active_element_set);
428     xbt_swag_foreach(_elem, elem_list) {
429       lmm_element_t elem = (lmm_element_t)_elem;
430       //Visiting active_element_set, so, by construction, should never get a zero weight, correct?
431       xbt_assert(elem->variable->sharing_weight > 0);
432       if (elem->consumption_weight > 0)
433         xbt_swag_insert(elem->variable, &(sys->saturated_variable_set));
434     }
435   }
436 }
437
438 void s_lmm_system_t::print()
439 {
440   std::string buf       = std::string("MAX-MIN ( ");
441   void* _var;
442
443   /* Printing Objective */
444   xbt_swag_t var_list = &variable_set;
445   xbt_swag_foreach(_var, var_list) {
446     lmm_variable_t var = (lmm_variable_t)_var;
447     buf = buf + "'" + std::to_string(var->id_int) + "'(" + std::to_string(var->sharing_weight) + ") ";
448   }
449   buf += ")";
450   XBT_DEBUG("%20s", buf.c_str());
451   buf.clear();
452
453   XBT_DEBUG("Constraints");
454   /* Printing Constraints */
455   void* _cnst;
456   xbt_swag_t cnst_list = &active_constraint_set;
457   xbt_swag_foreach(_cnst, cnst_list) {
458     lmm_constraint_t cnst = (lmm_constraint_t)_cnst;
459     double sum = 0.0;
460     //Show  the enabled variables
461     void* _elem;
462     xbt_swag_t elem_list = &(cnst->enabled_element_set);
463     buf += "\t";
464     buf += ((cnst->sharing_policy) ? "(" : "max(");
465     xbt_swag_foreach(_elem, elem_list) {
466       lmm_element_t elem = (lmm_element_t)_elem;
467       buf  = buf + std::to_string(elem->consumption_weight) + ".'" + std::to_string(elem->variable->id_int) + "'(" +
468             std::to_string(elem->variable->value) + ")" + ((cnst->sharing_policy) ? " + " : " , ");
469       if(cnst->sharing_policy)
470         sum += elem->consumption_weight * elem->variable->value;
471       else
472         sum = std::max(sum, elem->consumption_weight * elem->variable->value);
473     }
474     //TODO: Adding disabled elements only for test compatibility, but do we really want them to be printed?
475     elem_list = &(cnst->disabled_element_set);
476     xbt_swag_foreach(_elem, elem_list) {
477       lmm_element_t elem = (lmm_element_t)_elem;
478       buf  = buf + std::to_string(elem->consumption_weight) + ".'" + std::to_string(elem->variable->id_int) + "'(" +
479             std::to_string(elem->variable->value) + ")" + ((cnst->sharing_policy) ? " + " : " , ");
480       if(cnst->sharing_policy)
481         sum += elem->consumption_weight * elem->variable->value;
482       else
483         sum = std::max(sum, elem->consumption_weight * elem->variable->value);
484     }
485
486     buf = buf + "0) <= " + std::to_string(cnst->bound) + " ('" + std::to_string(cnst->id_int) + "')";
487
488     if (not cnst->sharing_policy) {
489       buf += " [MAX-Constraint]";
490     }
491     XBT_DEBUG("%s", buf.c_str());
492     buf.clear();
493     xbt_assert(not double_positive(sum - cnst->bound, cnst->bound * sg_maxmin_precision),
494                "Incorrect value (%f is not smaller than %f): %g", sum, cnst->bound, sum - cnst->bound);
495   }
496
497   XBT_DEBUG("Variables");
498   /* Printing Result */
499   xbt_swag_foreach(_var, var_list) {
500     lmm_variable_t var = (lmm_variable_t)_var;
501     if (var->bound > 0) {
502       XBT_DEBUG("'%d'(%f) : %f (<=%f)", var->id_int, var->sharing_weight, var->value, var->bound);
503       xbt_assert(not double_positive(var->value - var->bound, var->bound * sg_maxmin_precision),
504                  "Incorrect value (%f is not smaller than %f", var->value, var->bound);
505     } else {
506       XBT_DEBUG("'%d'(%f) : %f", var->id_int, var->sharing_weight, var->value);
507     }
508   }
509 }
510
511 void s_lmm_system_t::solve()
512 {
513   void* _cnst;
514   void* _cnst_next;
515   void* _elem;
516   double min_usage = -1;
517   double min_bound = -1;
518
519   if (not modified)
520     return;
521
522   XBT_IN("(sys=%p)", this);
523
524   /* Compute Usage and store the variables that reach the maximum. If selective_update_active is true, only constraints
525    * that changed are considered. Otherwise all constraints with active actions are considered.
526    */
527   xbt_swag_t cnst_list = selective_update_active ? &modified_constraint_set : &active_constraint_set;
528
529   XBT_DEBUG("Active constraints : %d", xbt_swag_size(cnst_list));
530   /* Init: Only modified code portions: reset the value of active variables */
531   xbt_swag_foreach(_cnst, cnst_list) {
532     lmm_constraint_t cnst = (lmm_constraint_t)_cnst;
533     xbt_swag_t elem_list  = &(cnst->enabled_element_set);
534     xbt_swag_foreach(_elem, elem_list) {
535       lmm_variable_t var = ((lmm_element_t)_elem)->variable;
536       xbt_assert(var->sharing_weight > 0.0);
537       var->value = 0.0;
538     }
539   }
540
541   s_lmm_constraint_light_t* cnst_light_tab = new s_lmm_constraint_light_t[xbt_swag_size(cnst_list)]();
542   int cnst_light_num = 0;
543   dyn_light_t saturated_constraints;
544
545   xbt_swag_foreach_safe(_cnst, _cnst_next, cnst_list) {
546     lmm_constraint_t cnst = (lmm_constraint_t)_cnst;
547     /* INIT: Collect constraints that actually need to be saturated (i.e remaining  and usage are strictly positive)
548      * into cnst_light_tab. */
549     cnst->remaining = cnst->bound;
550     if (not double_positive(cnst->remaining, cnst->bound * sg_maxmin_precision))
551       continue;
552     cnst->usage = 0;
553     xbt_swag_t elem_list = &(cnst->enabled_element_set);
554     xbt_swag_foreach(_elem, elem_list) {
555       lmm_element_t elem = (lmm_element_t)_elem;
556       xbt_assert(elem->variable->sharing_weight > 0);
557       if (elem->consumption_weight > 0) {
558         if (cnst->sharing_policy)
559           cnst->usage += elem->consumption_weight / elem->variable->sharing_weight;
560         else if (cnst->usage < elem->consumption_weight / elem->variable->sharing_weight)
561           cnst->usage = elem->consumption_weight / elem->variable->sharing_weight;
562
563         elem->make_active();
564         simgrid::surf::Action *action = static_cast<simgrid::surf::Action*>(elem->variable->id);
565         if (keep_track && not action->is_linked())
566           keep_track->push_back(*action);
567       }
568     }
569     XBT_DEBUG("Constraint '%d' usage: %f remaining: %f concurrency: %i<=%i<=%i", cnst->id_int, cnst->usage,
570               cnst->remaining, cnst->concurrency_current, cnst->concurrency_maximum, cnst->get_concurrency_limit());
571     /* Saturated constraints update */
572
573     if(cnst->usage > 0) {
574       cnst_light_tab[cnst_light_num].cnst = cnst;
575       cnst->cnst_light = &(cnst_light_tab[cnst_light_num]);
576       cnst_light_tab[cnst_light_num].remaining_over_usage = cnst->remaining / cnst->usage;
577       saturated_constraints_update(cnst_light_tab[cnst_light_num].remaining_over_usage, cnst_light_num,
578                                    saturated_constraints, &min_usage);
579       xbt_assert(cnst->active_element_set.count>0, "There is no sense adding a constraint that has no active element!");
580       cnst_light_num++;
581     }
582   }
583
584   saturated_variable_set_update(cnst_light_tab, saturated_constraints, this);
585
586   /* Saturated variables update */
587   do {
588     /* Fix the variables that have to be */
589     xbt_swag_t var_list = &saturated_variable_set;
590     void* _var;
591     lmm_variable_t var = nullptr;
592     xbt_swag_foreach(_var, var_list) {
593       var = (lmm_variable_t)_var;
594       if (var->sharing_weight <= 0.0)
595         DIE_IMPOSSIBLE;
596       /* First check if some of these variables could reach their upper bound and update min_bound accordingly. */
597       XBT_DEBUG("var=%d, var->bound=%f, var->weight=%f, min_usage=%f, var->bound*var->weight=%f", var->id_int,
598                 var->bound, var->sharing_weight, min_usage, var->bound * var->sharing_weight);
599       if ((var->bound > 0) && (var->bound * var->sharing_weight < min_usage)) {
600         if (min_bound < 0)
601           min_bound = var->bound * var->sharing_weight;
602         else
603           min_bound = std::min(min_bound, (var->bound * var->sharing_weight));
604         XBT_DEBUG("Updated min_bound=%f", min_bound);
605       }
606     }
607
608     while ((var = (lmm_variable_t)xbt_swag_getFirst(var_list))) {
609       if (min_bound < 0) {
610         //If no variable could reach its bound, deal iteratively the constraints usage ( at worst one constraint is
611         // saturated at each cycle)
612         var->value = min_usage / var->sharing_weight;
613         XBT_DEBUG("Setting var (%d) value to %f\n", var->id_int, var->value);
614       } else {
615          //If there exist a variable that can reach its bound, only update it (and other with the same bound) for now.
616          if (double_equals(min_bound, var->bound * var->sharing_weight, sg_maxmin_precision)) {
617            var->value = var->bound;
618            XBT_DEBUG("Setting %p (%d) value to %f\n", var, var->id_int, var->value);
619          } else {
620            // Variables which bound is different are not considered for this cycle, but they will be afterwards.
621            XBT_DEBUG("Do not consider %p (%d) \n", var, var->id_int);
622            xbt_swag_remove(var, var_list);
623            continue;
624          }
625       }
626       XBT_DEBUG("Min usage: %f, Var(%d)->weight: %f, Var(%d)->value: %f ", min_usage, var->id_int, var->sharing_weight,
627                 var->id_int, var->value);
628
629       /* Update the usage of contraints where this variable is involved */
630       for (s_lmm_element_t& elem : var->cnsts) {
631         lmm_constraint_t cnst = elem.constraint;
632         if (cnst->sharing_policy) {
633           // Remember: shared constraints require that sum(elem.value * var->value) < cnst->bound
634           double_update(&(cnst->remaining), elem.consumption_weight * var->value, cnst->bound * sg_maxmin_precision);
635           double_update(&(cnst->usage), elem.consumption_weight / var->sharing_weight, sg_maxmin_precision);
636           // If the constraint is saturated, remove it from the set of active constraints (light_tab)
637           if (not double_positive(cnst->usage, sg_maxmin_precision) ||
638               not double_positive(cnst->remaining, cnst->bound * sg_maxmin_precision)) {
639             if (cnst->cnst_light) {
640               int index = (cnst->cnst_light-cnst_light_tab);
641               XBT_DEBUG("index: %d \t cnst_light_num: %d \t || usage: %f remaining: %f bound: %f  ",
642                          index,cnst_light_num, cnst->usage, cnst->remaining, cnst->bound);
643               cnst_light_tab[index]=cnst_light_tab[cnst_light_num-1];
644               cnst_light_tab[index].cnst->cnst_light = &cnst_light_tab[index];
645               cnst_light_num--;
646               cnst->cnst_light = nullptr;
647             }
648           } else {
649             cnst->cnst_light->remaining_over_usage = cnst->remaining / cnst->usage;
650           }
651           elem.make_inactive();
652         } else {
653           // Remember: non-shared constraints only require that max(elem.value * var->value) < cnst->bound
654           cnst->usage = 0.0;
655           elem.make_inactive();
656           xbt_swag_t elem_list = &(cnst->enabled_element_set);
657           xbt_swag_foreach(_elem, elem_list) {
658             lmm_element_t elem2 = static_cast<lmm_element_t>(_elem);
659             xbt_assert(elem2->variable->sharing_weight > 0);
660             if (elem2->variable->value > 0)
661               continue;
662             if (elem2->consumption_weight > 0)
663               cnst->usage = std::max(cnst->usage, elem2->consumption_weight / elem2->variable->sharing_weight);
664           }
665           //If the constraint is saturated, remove it from the set of active constraints (light_tab)
666           if (not double_positive(cnst->usage, sg_maxmin_precision) ||
667               not double_positive(cnst->remaining, cnst->bound * sg_maxmin_precision)) {
668             if(cnst->cnst_light) {
669               int index = (cnst->cnst_light-cnst_light_tab);
670               XBT_DEBUG("index: %d \t cnst_light_num: %d \t || \t cnst: %p \t cnst->cnst_light: %p "
671                         "\t cnst_light_tab: %p usage: %f remaining: %f bound: %f  ", index,cnst_light_num, cnst,
672                         cnst->cnst_light, cnst_light_tab, cnst->usage, cnst->remaining, cnst->bound);
673               cnst_light_tab[index]=cnst_light_tab[cnst_light_num-1];
674               cnst_light_tab[index].cnst->cnst_light = &cnst_light_tab[index];
675               cnst_light_num--;
676               cnst->cnst_light = nullptr;
677             }
678           } else {
679             cnst->cnst_light->remaining_over_usage = cnst->remaining / cnst->usage;
680             xbt_assert(cnst->active_element_set.count>0, "Should not keep a maximum constraint that has no active"
681                        " element! You want to check the maxmin precision and possible rounding effects." );
682           }
683         }
684       }
685       xbt_swag_remove(var, var_list);
686     }
687
688     /* Find out which variables reach the maximum */
689     min_usage = -1;
690     min_bound = -1;
691     saturated_constraints.clear();
692     int pos;
693     for(pos=0; pos<cnst_light_num; pos++){
694       xbt_assert(cnst_light_tab[pos].cnst->active_element_set.count>0, "Cannot saturate more a constraint that has"
695                  " no active element! You may want to change the maxmin precision (--cfg=maxmin/precision:<new_value>)"
696                  " because of possible rounding effects.\n\tFor the record, the usage of this constraint is %g while "
697                  "the maxmin precision to which it is compared is %g.\n\tThe usage of the previous constraint is %g.",
698                  cnst_light_tab[pos].cnst->usage, sg_maxmin_precision, cnst_light_tab[pos-1].cnst->usage);
699       saturated_constraints_update(cnst_light_tab[pos].remaining_over_usage, pos, saturated_constraints, &min_usage);
700     }
701
702     saturated_variable_set_update(cnst_light_tab, saturated_constraints, this);
703
704   } while (cnst_light_num > 0);
705
706   modified = false;
707   if (selective_update_active)
708     remove_all_modified_set();
709
710   if (XBT_LOG_ISENABLED(surf_maxmin, xbt_log_priority_debug)) {
711     print();
712   }
713
714   check_concurrency();
715
716   delete[] cnst_light_tab;
717   XBT_OUT();
718 }
719
720 void lmm_solve(lmm_system_t sys)
721 {
722   sys->solve();
723 }
724
725 /** \brief Attribute the value bound to var->bound.
726  *
727  *  \param sys the lmm_system_t
728  *  \param var the lmm_variable_t
729  *  \param bound the new bound to associate with var
730  *
731  *  Makes var->bound equal to bound. Whenever this function is called a change is  signed in the system. To
732  *  avoid false system changing detection it is a good idea to test (bound != 0) before calling it.
733  */
734 void s_lmm_system_t::update_variable_bound(lmm_variable_t var, double bound)
735 {
736   modified   = true;
737   var->bound = bound;
738
739   if (not var->cnsts.empty())
740     update_modified_set(var->cnsts[0].constraint);
741 }
742
743 void s_lmm_variable_t::initialize(simgrid::surf::Action* id_value, double sharing_weight_value, double bound_value,
744                                   int number_of_constraints, unsigned visited_value)
745 {
746   id     = id_value;
747   id_int = s_lmm_variable_t::Global_debug_id++;
748   cnsts.reserve(number_of_constraints);
749   sharing_weight    = sharing_weight_value;
750   staged_weight     = 0.0;
751   bound             = bound_value;
752   concurrency_share = 1;
753   value             = 0.0;
754   visited           = visited_value;
755   mu                = 0.0;
756   new_mu            = 0.0;
757   func_f            = func_f_def;
758   func_fp           = func_fp_def;
759   func_fpi          = func_fpi_def;
760
761   variable_set_hookup.next           = nullptr;
762   variable_set_hookup.prev           = nullptr;
763   saturated_variable_set_hookup.next = nullptr;
764   saturated_variable_set_hookup.prev = nullptr;
765 }
766
767 int s_lmm_variable_t::get_min_concurrency_slack() const
768 {
769   int minslack = std::numeric_limits<int>::max();
770   for (s_lmm_element_t const& elem : cnsts) {
771     int slack = elem.constraint->get_concurrency_slack();
772     if (slack < minslack) {
773       // This is only an optimization, to avoid looking at more constraints when slack is already zero
774       if (slack == 0)
775         return 0;
776       minslack = slack;
777     }
778   }
779   return minslack;
780 }
781
782 //Small remark: In this implementation of lmm_enable_var and lmm_disable_var, we will meet multiple times with var when
783 // running sys->update_modified_set.
784 // A priori not a big performance issue, but we might do better by calling sys->update_modified_set within the for loops
785 // (after doing the first for enabling==1, and before doing the last for disabling==1)
786 void s_lmm_system_t::enable_var(lmm_variable_t var)
787 {
788   xbt_assert(not XBT_LOG_ISENABLED(surf_maxmin, xbt_log_priority_debug) || var->can_enable());
789
790   var->sharing_weight = var->staged_weight;
791   var->staged_weight = 0;
792
793   // Enabling the variable, move to var to list head. Subtlety is: here, we need to call update_modified_set AFTER
794   // moving at least one element of var.
795
796   xbt_swag_remove(var, &variable_set);
797   xbt_swag_insert_at_head(var, &variable_set);
798   for (s_lmm_element_t& elem : var->cnsts) {
799     xbt_swag_remove(&elem, &(elem.constraint->disabled_element_set));
800     xbt_swag_insert_at_head(&elem, &(elem.constraint->enabled_element_set));
801     elem.increase_concurrency();
802   }
803   if (not var->cnsts.empty())
804     update_modified_set(var->cnsts[0].constraint);
805
806   // When used within on_disabled_var, we would get an assertion fail, because transiently there can be variables
807   // that are staged and could be activated.
808   // Anyway, caller functions all call check_concurrency() in the end.
809 }
810
811 void s_lmm_system_t::disable_var(lmm_variable_t var)
812 {
813   xbt_assert(not var->staged_weight, "Staged weight should have been cleared");
814   // Disabling the variable, move to var to list tail. Subtlety is: here, we need to call update_modified_set
815   // BEFORE moving the last element of var.
816   xbt_swag_remove(var, &variable_set);
817   xbt_swag_insert_at_tail(var, &variable_set);
818   if (not var->cnsts.empty())
819     update_modified_set(var->cnsts[0].constraint);
820   for (s_lmm_element_t& elem : var->cnsts) {
821     xbt_swag_remove(&elem, &(elem.constraint->enabled_element_set));
822     xbt_swag_insert_at_tail(&elem, &(elem.constraint->disabled_element_set));
823
824     xbt_swag_remove(&elem, &(elem.constraint->active_element_set));
825
826     elem.decrease_concurrency();
827   }
828
829   var->sharing_weight = 0.0;
830   var->staged_weight=0.0;
831   var->value = 0.0;
832   check_concurrency();
833 }
834
835 /* /brief Find variables that can be enabled and enable them.
836  *
837  * Assuming that the variable has already been removed from non-zero weights
838  * Can we find a staged variable to add?
839  * If yes, check that none of the constraints that this variable is involved in is at the limit of its concurrency
840  * And then add it to enabled variables
841  */
842 void s_lmm_system_t::on_disabled_var(lmm_constraint_t cnstr)
843 {
844   if (cnstr->get_concurrency_limit() < 0)
845     return;
846
847   int numelem = xbt_swag_size(&(cnstr->disabled_element_set));
848   if (not numelem)
849     return;
850
851   lmm_element_t elem = (lmm_element_t)xbt_swag_getFirst(&(cnstr->disabled_element_set));
852
853   //Cannot use xbt_swag_foreach, because lmm_enable_var will modify disabled_element_set.. within the loop
854   while (numelem-- && elem) {
855
856     lmm_element_t nextelem = (lmm_element_t)xbt_swag_getNext(elem, cnstr->disabled_element_set.offset);
857
858     if (elem->variable->staged_weight > 0 && elem->variable->can_enable()) {
859       //Found a staged variable
860       //TODOLATER: Add random timing function to model reservation protocol fuzziness? Then how to make sure that
861       //staged variables will eventually be called?
862       enable_var(elem->variable);
863     }
864
865     xbt_assert(cnstr->concurrency_current <= cnstr->get_concurrency_limit(), "Concurrency overflow!");
866     if (cnstr->concurrency_current == cnstr->get_concurrency_limit())
867       break;
868
869     elem = nextelem;
870   }
871
872   //We could get an assertion fail, because transiently there can be variables that are staged and could be activated.
873   //And we need to go through all constraints of the disabled var before getting back a coherent state.
874   // Anyway, caller functions all call check_concurrency() in the end.
875 }
876
877 /* \brief update the weight of a variable, and enable/disable it.
878  * @return Returns whether a change was made
879  */
880 void s_lmm_system_t::update_variable_weight(lmm_variable_t var, double weight)
881 {
882   xbt_assert(weight>=0,"Variable weight should not be negative!");
883
884   if (weight == var->sharing_weight)
885     return;
886
887   int enabling_var  = (weight > 0 && var->sharing_weight <= 0);
888   int disabling_var = (weight <= 0 && var->sharing_weight > 0);
889
890   XBT_IN("(sys=%p, var=%p, weight=%f)", this, var, weight);
891
892   modified = true;
893
894   //Are we enabling this variable?
895   if (enabling_var){
896     var->staged_weight = weight;
897     int minslack       = var->get_min_concurrency_slack();
898     if (minslack < var->concurrency_share) {
899       XBT_DEBUG("Staging var (instead of enabling) because min concurrency slack %i, with weight %f and concurrency"
900                 " share %i", minslack, weight, var->concurrency_share);
901       return;
902     }
903     XBT_DEBUG("Enabling var with min concurrency slack %i", minslack);
904     enable_var(var);
905   } else if (disabling_var){
906     //Are we disabling this variable?
907     disable_var(var);
908   } else {
909     var->sharing_weight = weight;
910   }
911
912   check_concurrency();
913
914   XBT_OUT();
915 }
916
917 void s_lmm_system_t::update_constraint_bound(lmm_constraint_t cnst, double bound)
918 {
919   modified = true;
920   update_modified_set(cnst);
921   cnst->bound = bound;
922 }
923
924 /** \brief Update the constraint set propagating recursively to other constraints so the system should not be entirely
925  *  computed.
926  *
927  *  \param sys the lmm_system_t
928  *  \param cnst the lmm_constraint_t affected by the change
929  *
930  *  A recursive algorithm to optimize the system recalculation selecting only constraints that have changed. Each
931  *  constraint change is propagated to the list of constraints for each variable.
932  */
933 void s_lmm_system_t::update_modified_set_rec(lmm_constraint_t cnst)
934 {
935   void* _elem;
936
937   xbt_swag_foreach(_elem, &cnst->enabled_element_set) {
938     lmm_variable_t var = ((lmm_element_t)_elem)->variable;
939     for (s_lmm_element_t const& elem : var->cnsts) {
940       if (var->visited == visited_counter)
941         break;
942       if (elem.constraint != cnst && not xbt_swag_belongs(elem.constraint, &modified_constraint_set)) {
943         xbt_swag_insert(elem.constraint, &modified_constraint_set);
944         update_modified_set_rec(elem.constraint);
945       }
946     }
947     //var will be ignored in later visits as long as sys->visited_counter does not move
948     var->visited = visited_counter;
949   }
950 }
951
952 void s_lmm_system_t::update_modified_set(lmm_constraint_t cnst)
953 {
954   /* nothing to do if selective update isn't active */
955   if (selective_update_active && not xbt_swag_belongs(cnst, &modified_constraint_set)) {
956     xbt_swag_insert(cnst, &modified_constraint_set);
957     update_modified_set_rec(cnst);
958   }
959 }
960
961 void s_lmm_system_t::remove_all_modified_set()
962 {
963   // We cleverly un-flag all variables just by incrementing visited_counter
964   // In effect, the var->visited value will no more be equal to visited counter
965   // To be clean, when visited counter has wrapped around, we force these var->visited values so that variables that
966   // were in the modified a long long time ago are not wrongly skipped here, which would lead to very nasty bugs
967   // (i.e. not readibily reproducible, and requiring a lot of run time before happening).
968   if (++visited_counter == 1) {
969     /* the counter wrapped around, reset each variable->visited */
970     void *_var;
971     xbt_swag_foreach(_var, &variable_set)
972       ((lmm_variable_t)_var)->visited = 0;
973   }
974   xbt_swag_reset(&modified_constraint_set);
975 }
976
977 /**
978  * Returns resource load (in flop per second, or byte per second, or similar)
979  *
980  * If the resource is shared (the default case), the load is sum of resource usage made by every variables located on
981  * this resource.
982  *
983  * If the resource is not shared (ie in FATPIPE mode), then the load is the max (not the sum) of all resource usages
984  * located on this resource.
985  *
986  * \param cnst the lmm_constraint_t associated to the resource
987  */
988 double s_lmm_constraint_t::get_usage() const
989 {
990   double result              = 0.0;
991   const_xbt_swag_t elem_list = &enabled_element_set;
992   void* _elem;
993
994   xbt_swag_foreach(_elem, elem_list)
995   {
996     lmm_element_t elem = (lmm_element_t)_elem;
997     if (elem->consumption_weight > 0) {
998       if (sharing_policy)
999         result += elem->consumption_weight * elem->variable->value;
1000       else if (result < elem->consumption_weight * elem->variable->value)
1001         result = std::max(result, elem->consumption_weight * elem->variable->value);
1002     }
1003   }
1004   return result;
1005 }
1006
1007 int s_lmm_constraint_t::get_variable_amount() const
1008 {
1009   int result                 = 0;
1010   const_xbt_swag_t elem_list = &enabled_element_set;
1011   void *_elem;
1012
1013   xbt_swag_foreach(_elem, elem_list) {
1014     lmm_element_t elem = (lmm_element_t)_elem;
1015     if (elem->consumption_weight > 0)
1016       result++;
1017   }
1018   return result;
1019 }