Logo AND Algorithmique Numérique Distribuée

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