1 /* Copyright (c) 2019-2023. The SimGrid Team. All rights reserved. */
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. */
6 #include "src/include/catch.hpp"
7 #include "src/kernel/lmm/bmf.hpp"
10 namespace lmm = simgrid::kernel::lmm;
12 TEST_CASE("kernel::bmf Basic tests", "[kernel-bmf-basic]")
14 lmm::BmfSystem Sys(false);
16 SECTION("Single flow")
19 * A single variable using a single resource
22 * o System: a1 * p1 * \rho1 < C
23 * o consumption_weight: a1=1
24 * o sharing_penalty: p1=1
30 lmm::Constraint* sys_cnst = Sys.constraint_new(nullptr, 3);
31 lmm::Variable* rho_1 = Sys.variable_new(nullptr, 1);
33 Sys.expand(sys_cnst, rho_1, 1);
36 REQUIRE(double_equals(rho_1->get_value(), 3, sg_precision_workamount));
42 * Two flows sharing a single resource
45 * o System: a1 * p1 * \rho1 + a2 * p2 * \rho2 < C
46 * o consumption_weight: a1=1 ; a2=10
47 * o sharing_penalty: p1=1 ; p2=1
54 lmm::Constraint* sys_cnst = Sys.constraint_new(nullptr, 3);
55 lmm::Variable* rho_1 = Sys.variable_new(nullptr, 1);
56 lmm::Variable* rho_2 = Sys.variable_new(nullptr, 1);
58 Sys.expand(sys_cnst, rho_1, 1);
59 Sys.expand(sys_cnst, rho_2, 10);
62 REQUIRE(double_equals(rho_1->get_value(), 3.0 / 2.0, sg_precision_workamount));
63 REQUIRE(double_equals(rho_2->get_value(), (3.0 / 2.0) / 10.0, sg_precision_workamount));
66 SECTION("Variable penalty/priority")
69 * A variable with twice the penalty gets half of the share
72 * o System: a1 * p1 * \rho1 + a2 * p2 * \rho2 < C
73 * o consumption_weight: a1=1 ; a2=1
74 * o sharing_penalty: p1=1 ; p2=2
77 * o rho1 = 2* rho2 (because rho2 has twice the penalty)
78 * o rho1 + rho2 = C (because all weights are 1)
81 lmm::Constraint* sys_cnst = Sys.constraint_new(nullptr, 1);
82 lmm::Variable* rho_1 = Sys.variable_new(nullptr, 1);
83 lmm::Variable* rho_2 = Sys.variable_new(nullptr, 2);
85 Sys.expand(sys_cnst, rho_1, 1);
86 Sys.expand(sys_cnst, rho_2, 1);
89 REQUIRE(double_equals(rho_1->get_value(), 2.0 / 3.0, sg_precision_workamount));
90 REQUIRE(double_equals(rho_2->get_value(), 1.0 / 3.0, sg_precision_workamount));
93 SECTION("Disable variable doesn't count")
96 * Two flows sharing a single resource, but one is disabled
99 * o System: a1 * p1 * \rho1 + a2 * p2 * \rho2 < C
100 * o consumption_weight: a1=1 ; a2=10
101 * o sharing_penalty: p1=1 ; p2=0
107 lmm::Constraint* sys_cnst = Sys.constraint_new(nullptr, 1);
108 lmm::Variable* rho_1 = Sys.variable_new(nullptr, 1);
109 lmm::Variable* rho_2 = Sys.variable_new(nullptr, 0);
111 Sys.expand(sys_cnst, rho_1, 1);
112 Sys.expand(sys_cnst, rho_2, 10);
115 REQUIRE(double_equals(rho_1->get_value(), 1.0, sg_precision_workamount));
116 REQUIRE(double_equals(rho_2->get_value(), 0.0, sg_precision_workamount));
119 SECTION("No consumption variable")
122 * An empty variable, no consumption, just assure it receives something
124 * o System: a1 * p1 * \rho1 < C
125 * o consumption_weight: a1=0
126 * o sharing_penalty: p1=1
132 lmm::Constraint* sys_cnst = Sys.constraint_new(nullptr, 3);
133 lmm::Variable* rho_1 = Sys.variable_new(nullptr, 1);
134 lmm::Variable* rho_2 = Sys.variable_new(nullptr, 0);
136 Sys.expand(sys_cnst, rho_1, 1);
137 Sys.expand(sys_cnst, rho_2, 10);
140 REQUIRE(double_positive(rho_1->get_value(), sg_precision_workamount));
143 SECTION("Bounded variable")
146 * Assures a player receives the min(bound, share) if it's bounded
148 * o System: a1 * p1 * \rho1 + a2 * p2 * \rho2 < C
149 * o bounds: b1=0.1, b2=-1
150 * o consumption_weight: a1=1, a2=1
151 * o sharing_penalty: p1=1, p2=1
158 lmm::Constraint* sys_cnst = Sys.constraint_new(nullptr, 1);
159 lmm::Variable* rho_1 = Sys.variable_new(nullptr, 1, .1);
160 lmm::Variable* rho_2 = Sys.variable_new(nullptr, 1);
162 Sys.expand(sys_cnst, rho_1, 2);
163 Sys.expand(sys_cnst, rho_2, 1);
165 REQUIRE(double_equals(rho_1->get_value(), .1, sg_precision_workamount));
166 REQUIRE(double_equals(rho_2->get_value(), .8, sg_precision_workamount));
172 * Two flows using a fatpipe resource
175 * o System: a1 * p1 * \rho1 < C and a2 * p2 * \rho2 < C
176 * o consumption_weight: a1=1 ; a2=1
177 * o sharing_penalty: p1=1 ; p2=1
184 lmm::Constraint* sys_cnst = Sys.constraint_new(nullptr, 3);
185 sys_cnst->set_sharing_policy(lmm::Constraint::SharingPolicy::FATPIPE, {});
186 lmm::Variable* rho_1 = Sys.variable_new(nullptr, 1);
187 lmm::Variable* rho_2 = Sys.variable_new(nullptr, 1);
189 Sys.expand(sys_cnst, rho_1, 1);
190 Sys.expand(sys_cnst, rho_2, 1);
193 REQUIRE(double_equals(rho_1->get_value(), 3.0, sg_precision_workamount));
194 REQUIRE(double_equals(rho_2->get_value(), 3.0, sg_precision_workamount));
197 SECTION("(un)Bounded variable")
200 * Assures a player receives the share if bound is greater than share
202 * o System: a1 * p1 * \rho1 + a2 * p2 * \rho2 < C
203 * o bounds: b1=1, b2=-1
204 * o consumption_weight: a1=1, a2=1
205 * o sharing_penalty: p1=1, p2=1
212 lmm::Constraint* sys_cnst = Sys.constraint_new(nullptr, 1);
213 lmm::Variable* rho_1 = Sys.variable_new(nullptr, 1, 1);
214 lmm::Variable* rho_2 = Sys.variable_new(nullptr, 1);
216 Sys.expand(sys_cnst, rho_1, 1);
217 Sys.expand(sys_cnst, rho_2, 1);
219 REQUIRE(double_equals(rho_1->get_value(), .5, sg_precision_workamount));
220 REQUIRE(double_equals(rho_2->get_value(), .5, sg_precision_workamount));
223 SECTION("Dynamic bounds")
226 * Resource bound is modified by user callback and shares are adapted accordingly
228 * o System: a1 * p1 * \rho1 + a2 * p2 * \rho2 < C
229 * o consumption_weight: a1=1, a2=1
230 * o sharing_penalty: p1=1, p2=1
233 * o rho1 = .5 and .25
237 lmm::Constraint* sys_cnst = Sys.constraint_new(nullptr, 1);
238 sys_cnst->set_sharing_policy(lmm::Constraint::SharingPolicy::NONLINEAR,
239 [](double bound, int n) { return bound / n; });
240 // alone, full capacity
241 lmm::Variable* rho_1 = Sys.variable_new(nullptr, 1);
242 Sys.expand(sys_cnst, rho_1, 1);
244 REQUIRE(double_equals(rho_1->get_value(), 1, sg_precision_workamount));
246 // add another variable, half initial capacity
247 lmm::Variable* rho_2 = Sys.variable_new(nullptr, 1);
248 Sys.expand(sys_cnst, rho_2, 1);
251 REQUIRE(double_equals(rho_1->get_value(), .25, sg_precision_workamount));
252 REQUIRE(double_equals(rho_2->get_value(), .25, sg_precision_workamount));
255 Sys.variable_free_all();
258 TEST_CASE("kernel::bmf Advanced tests", "[kernel-bmf-advanced]")
260 lmm::BmfSystem Sys(false);
262 SECTION("2 flows, 2 resources")
265 * Two flows sharing 2 resources with opposite requirements
268 * o System: a1 * p1 * \rho1 + a2 * p2 * \rho2 < C1
269 * o System: a1 * p1 * \rho1 + a2 * p2 * \rho2 < C2
271 * o consumption_weight: a11=1, a12=10, a21=10, a22=1
272 * o sharing_penalty: p1=1, p2=1
275 * o rho1 = rho2 = C/11
278 * [1 10] * [rho1 rho2] = [1]
282 lmm::Constraint* sys_cnst = Sys.constraint_new(nullptr, 1);
283 lmm::Constraint* sys_cnst2 = Sys.constraint_new(nullptr, 1);
284 lmm::Variable* rho_1 = Sys.variable_new(nullptr, 1, -1, 2);
285 lmm::Variable* rho_2 = Sys.variable_new(nullptr, 1, -1, 2);
287 Sys.expand(sys_cnst, rho_1, 1);
288 Sys.expand(sys_cnst2, rho_1, 10);
289 Sys.expand(sys_cnst, rho_2, 10);
290 Sys.expand(sys_cnst2, rho_2, 1);
293 REQUIRE(double_equals(rho_1->get_value(), 1.0 / 11.0, sg_precision_workamount));
294 REQUIRE(double_equals(rho_2->get_value(), 1.0 / 11.0, sg_precision_workamount));
297 SECTION("BMF paper example")
300 * 3 flows sharing 3 resources
303 * [1 1 1/2] * [rho1 rho2 rho3] = [1]
307 * Expectations (several possible BMF allocations, our algorithm return this)
308 * o rho1 = rho2 = rho3 = 2/5
311 lmm::Constraint* sys_cnst = Sys.constraint_new(nullptr, 1);
312 lmm::Constraint* sys_cnst2 = Sys.constraint_new(nullptr, 1);
313 lmm::Constraint* sys_cnst3 = Sys.constraint_new(nullptr, 1);
314 lmm::Variable* rho_1 = Sys.variable_new(nullptr, 1, -1, 3);
315 lmm::Variable* rho_2 = Sys.variable_new(nullptr, 1, -1, 3);
316 lmm::Variable* rho_3 = Sys.variable_new(nullptr, 1, -1, 3);
318 Sys.expand(sys_cnst3, rho_1, 1.0); // put this expand first to force a singular A' matrix
319 Sys.expand(sys_cnst, rho_1, 1.0);
320 Sys.expand(sys_cnst2, rho_1, 1.0);
321 Sys.expand(sys_cnst, rho_2, 1.0);
322 Sys.expand(sys_cnst2, rho_2, 1.0 / 2.0);
323 Sys.expand(sys_cnst3, rho_2, 3.0 / 4.0);
324 Sys.expand(sys_cnst, rho_3, 1.0 / 2.0);
325 Sys.expand(sys_cnst2, rho_3, 1.0);
326 Sys.expand(sys_cnst3, rho_3, 3.0 / 4.0);
329 REQUIRE(double_equals(rho_1->get_value(), 1.0 / 3.0, sg_precision_workamount));
330 REQUIRE(double_equals(rho_2->get_value(), 4.0 / 9.0, sg_precision_workamount));
331 REQUIRE(double_equals(rho_3->get_value(), 4.0 / 9.0, sg_precision_workamount));
334 SECTION("IO - example")
337 * Two flows sharing 1 disk
338 * read, write and readwrite constraint
341 * o System: a1 * p1 * \rho1 + a2 * p2 * \rho2 < C1
342 * o System: a1 * p1 * \rho1 + a2 * p2 * \rho2 < C2
343 * o System: a1 * p1 * \rho1 + a2 * p2 * \rho2 < C3
345 * o consumption_weight: a1=1, a2=1
346 * o sharing_penalty: p1=1, p2=1
349 * o rho1 = rho2 = C/2
352 * [1 10] * [rho1 rho2] = [1]
356 lmm::Constraint* sys_cnst = Sys.constraint_new(nullptr, 1e6);
357 lmm::Constraint* sys_cnst2 = Sys.constraint_new(nullptr, 1e6);
358 lmm::Constraint* sys_cnst3 = Sys.constraint_new(nullptr, 1e6);
359 lmm::Variable* rho_1 = Sys.variable_new(nullptr, 1, -1, 3);
360 lmm::Variable* rho_2 = Sys.variable_new(nullptr, 1, -1, 3);
362 /* A' and C' matrices are dependent on the order of initialization
363 * this order is needed to identify a bug in the solver */
364 Sys.expand(sys_cnst2, rho_2, 1);
365 Sys.expand(sys_cnst, rho_1, 1);
366 Sys.expand(sys_cnst3, rho_1, 1);
367 Sys.expand(sys_cnst3, rho_2, 1);
370 REQUIRE(double_equals(rho_1->get_value(), 1e6 / 2.0, sg_precision_workamount));
371 REQUIRE(double_equals(rho_2->get_value(), 1e6 / 2.0, sg_precision_workamount));
374 SECTION("Proportional fairness")
377 * 3 flows sharing 2 resources with crosstraffic
379 * Regular max-min would give B/2 for every flow.
380 * BMF is equivalent to proportional fairness in this case, and give a quite
385 lmm::Constraint* sys_cnst = Sys.constraint_new(nullptr, 1);
386 lmm::Constraint* sys_cnst2 = Sys.constraint_new(nullptr, 1);
387 lmm::Variable* rho_1 = Sys.variable_new(nullptr, 1, -1, 2);
388 lmm::Variable* rho_2 = Sys.variable_new(nullptr, 1, -1, 2);
389 lmm::Variable* rho_3 = Sys.variable_new(nullptr, 1, -1, 2);
391 double epsilon = 0.05;
392 Sys.expand(sys_cnst, rho_1, 1.0);
393 Sys.expand(sys_cnst2, rho_1, epsilon);
394 Sys.expand(sys_cnst, rho_2, 1.0);
395 Sys.expand(sys_cnst2, rho_2, epsilon);
396 Sys.expand(sys_cnst2, rho_3, 1.0);
397 Sys.expand(sys_cnst, rho_3, epsilon);
400 REQUIRE(double_equals(rho_1->get_value(), 1.0 / (2.0 + 2 * epsilon), sg_precision_workamount));
401 REQUIRE(double_equals(rho_2->get_value(), 1.0 / (2.0 + 2 * epsilon), sg_precision_workamount));
402 REQUIRE(double_equals(rho_3->get_value(), 1.0 / (1.0 + epsilon), sg_precision_workamount));
405 Sys.variable_free_all();
408 TEST_CASE("kernel::bmf Subflows", "[kernel-bmf-subflow]")
410 lmm::BmfSystem Sys(false);
412 SECTION("2 subflows and 1 resource")
415 * 2 identical flows composed of 2 subflows
417 * They must receive the same share and use same amount of resources
420 * o System: a1 * p1 * \rho1 + a2 * p2 * \rho2 < C
421 * o consumption_weight: a11=5, a12=7, a2=7, a2=5
422 * o sharing_penalty: p1=1, p2=1
425 * o rho1 = rho2 = (C/2)/12
428 * [12 12] * [rho1 rho2] = [1]
432 lmm::Constraint* sys_cnst = Sys.constraint_new(nullptr, 5);
433 lmm::Variable* rho_1 = Sys.variable_new(nullptr, 1);
434 lmm::Variable* rho_2 = Sys.variable_new(nullptr, 1);
436 Sys.expand(sys_cnst, rho_1, 5);
437 Sys.expand(sys_cnst, rho_1, 7);
438 Sys.expand(sys_cnst, rho_2, 7);
439 Sys.expand(sys_cnst, rho_2, 5);
442 REQUIRE(double_equals(rho_1->get_value(), 5.0 / 24.0, sg_precision_workamount));
443 REQUIRE(double_equals(rho_2->get_value(), 5.0 / 24.0, sg_precision_workamount));
446 SECTION("1 subflows, 1 flow and 1 resource")
449 * 2 flows, 1 resource
450 * 1 flow composed of 2 subflows
452 * Same share/rho, but subflow uses 50% more resources since it has a second connection/subflow
455 * o System: a1 * p1 * \rho1 + a2 * p2 * \rho2 < C
456 * o consumption_weight: a11=10, a12=5 a2=10
457 * o sharing_penalty: p1=1, p2=1
464 * [15 10] * [rho1 rho2] = [1]
468 lmm::Constraint* sys_cnst = Sys.constraint_new(nullptr, 5);
469 lmm::Variable* rho_1 = Sys.variable_new(nullptr, 1);
470 lmm::Variable* rho_2 = Sys.variable_new(nullptr, 1);
472 Sys.expand(sys_cnst, rho_1, 10);
473 Sys.expand(sys_cnst, rho_1, 5);
474 Sys.expand(sys_cnst, rho_2, 10);
477 REQUIRE(double_equals(rho_1->get_value(), (5.0 / 25.0), sg_precision_workamount));
478 REQUIRE(double_equals(rho_2->get_value(), (5.0 / 25.0), sg_precision_workamount));
479 REQUIRE(double_equals(15 * rho_1->get_value(), 10 * rho_2->get_value() * 3 / 2, sg_precision_workamount));
482 SECTION("1 subflows using 2 resources: different max for each resource")
485 * Test condition that we may have different max for different resources
488 * o System: a1 * p1 * \rho1 + a2 * p2 * \rho2 < C
489 * o consumption_weight: a11=1, a12=1, a2=1
490 * o consumption_weight: a21=1/2, a12=1/2 a2=3/2
491 * o sharing_penalty: p1=1, p2=1
498 * [2 1 ] * [rho1 rho2] = [1]
502 lmm::Constraint* sys_cnst = Sys.constraint_new(nullptr, 1);
503 lmm::Constraint* sys_cnst2 = Sys.constraint_new(nullptr, 1);
504 lmm::Variable* rho_1 = Sys.variable_new(nullptr, 1, -1, 2);
505 lmm::Variable* rho_2 = Sys.variable_new(nullptr, 1, -1, 2);
507 Sys.expand(sys_cnst, rho_1, 1.0);
508 Sys.expand(sys_cnst, rho_1, 1.0);
509 Sys.expand(sys_cnst, rho_2, 1);
510 Sys.expand(sys_cnst2, rho_1, 1.0 / 2.0);
511 Sys.expand(sys_cnst2, rho_1, 1.0 / 2.0);
512 Sys.expand(sys_cnst2, rho_2, 3.0 / 2.0);
515 REQUIRE(double_equals(rho_1->get_value(), (1.0 / 3.0), sg_precision_workamount));
516 REQUIRE(double_equals(rho_2->get_value(), (1.0 / 3.0), sg_precision_workamount));
519 Sys.variable_free_all();
522 TEST_CASE("kernel::bmf Loop", "[kernel-bmf-loop]")
524 lmm::BmfSystem Sys(false);
526 SECTION("Initial allocation loops")
529 * Complex matrix whose initial allocation loops and is unable
530 * to stabilize after 10 iterations.
532 * The algorithm needs to restart from another point
535 std::vector<double> C = {1.0, 1.0, 1.0, 1.0, 1.0};
536 std::vector<std::vector<double>> A = {
537 {0.0918589, 0.980201, 0.553352, 0.0471331, 0.397493, 0.0494386, 0.158874, 0.737557, 0.822504, 0.364411},
538 {0.852866, 0.383171, 0.924183, 0.318345, 0.937625, 0.980201, 0.0471331, 0.0494386, 0.737557, 0.364411},
539 {0.12043, 0.985661, 0.153195, 0.852866, 0.247113, 0.318345, 0.0918589, 0.0471331, 0.158874, 0.364411},
540 {0.387291, 0.159939, 0.641492, 0.985661, 0.0540999, 0.383171, 0.318345, 0.980201, 0.0494386, 0.364411},
541 {0.722983, 0.924512, 0.474874, 0.819576, 0.572598, 0.0540999, 0.247113, 0.937625, 0.397493, 0.364411}};
543 std::vector<lmm::Constraint*> sys_cnst;
545 sys_cnst.push_back(Sys.constraint_new(nullptr, c));
547 std::vector<lmm::Variable*> vars;
548 std::for_each(A[0].begin(), A[0].end(),
549 [&vars, &Sys, &A](const auto&) { vars.push_back(Sys.variable_new(nullptr, 1, -1, A.size())); });
550 for (size_t j = 0; j < A.size(); j++) {
551 for (size_t i = 0; i < A[j].size(); i++) {
552 Sys.expand(sys_cnst[j], vars[i], A[j][i]);
557 for (const auto* rho : vars) {
558 REQUIRE(double_positive(rho->get_value(), sg_precision_workamount));
562 Sys.variable_free_all();
565 TEST_CASE("kernel::bmf Bugs", "[kernel-bmf-bug]")
567 lmm::BmfSystem Sys(false);
569 SECTION("DadOu's bug: sum of bounds/phi greater than C")
572 * Ptasks in a g5k platform.
573 * Extracted from original test.
574 * The sum of bounds for 1 resource exceed its capacity, giving a negative value in C'
577 lmm::Constraint* sys_cnst = Sys.constraint_new(nullptr, 2.5e9);
578 lmm::Constraint* sys_cnst2 = Sys.constraint_new(nullptr, 2.5e9);
579 lmm::Variable* rho_1 = Sys.variable_new(nullptr, 1, 2.27328e-10, 2);
580 lmm::Variable* rho_2 = Sys.variable_new(nullptr, 1, 2.27328e-10, 2);
581 lmm::Variable* rho_3 = Sys.variable_new(nullptr, 1);
583 Sys.expand(sys_cnst, rho_1, 1.84467e+19);
584 Sys.expand(sys_cnst2, rho_1, 1.84467e+19);
585 Sys.expand(sys_cnst, rho_2, 1.84467e+19);
586 Sys.expand(sys_cnst, rho_3, 1.91268e+11);
590 SECTION("DadOu's bug: complete matrix")
592 constexpr int cnsts = 71;
593 constexpr int flows = 3;
595 Eigen::MatrixXd A(cnsts, flows);
596 A << 0, 0, 0, 0, 0, 0, 0, 0, 1.84467e+19, 1.91268e+11, 1.84467e+19, 1.84467e+19, 0, 0, 1.84467e+19, 0, 1.84467e+19,
597 0, 0, 1.84467e+19, 0, 4.975e+11, 0, 0, 4.975e+11, 0, 0, 4.975e+11, 0, 0, 4.975e+11, 0, 0, 4.975e+11, 0, 0,
598 4.975e+11, 0, 0, 4.975e+11, 0, 0, 4.975e+11, 0, 0, 4.975e+11, 0, 0, 4.975e+11, 0, 0, 4.975e+11, 0, 0, 4.975e+11,
599 0, 0, 4.975e+11, 0, 0, 4.975e+11, 0, 0, 4.975e+11, 0, 0, 4.975e+11, 0, 0, 1.256e+09, 0, 0, 2.2372e+10, 0, 0,
600 2.2684e+10, 0, 0, 2.2588e+10, 0, 0, 2.3188e+10, 0, 0, 2.228e+10, 0, 0, 2.058e+10, 0, 0, 2.2684e+10, 0, 0,
601 2.2824e+10, 0, 0, 2.2976e+10, 0, 0, 2.2632e+10, 0, 0, 2.2584e+10, 0, 0, 2.2736e+10, 0, 0, 2.2616e+10, 0, 0,
602 2.0828e+10, 0, 0, 2.3184e+10, 0, 0, 2.2524e+10, 0, 0, 2.278e+10, 0, 0, 2.2164e+10, 0, 0, 1.26e+09, 0, 0,
603 2.1872e+10, 0, 0, 1.4e+09, 0, 0, 2.3184e+10, 0, 0, 8.52e+08, 0, 0, 2.2268e+10, 0, 0, 1.756e+09, 0, 0,
604 2.0636e+10, 0, 0, 3.4e+09, 0, 0, 2.2576e+10, 0, 0, 1.352e+09, 0, 0, 2.2832e+10, 0, 0, 1.2e+09, 0, 0, 2.3092e+10,
605 0, 0, 9.48e+08, 0, 0, 2.2436e+10, 0, 0, 1.4e+09, 0, 0, 2.2572e+10, 0, 0, 1.452e+09, 0, 0, 2.2692e+10, 0, 0,
606 1.3e+09, 0, 0, 2.2832e+10, 0, 0, 1.2e+09, 0, 0, 2.1232e+10, 0, 0, 2.804e+09, 0, 0, 2.3184e+10, 0, 0,
607 8.56001e+08, 0, 0, 2.2512e+10, 0, 0, 1.5e+09, 0, 0;
609 Eigen::MatrixXd maxA(cnsts, flows);
610 maxA << 0, 0, 0, 0, 0, 0, 0, 0, 1.84467e+19, 3.1e+09, 1.84467e+19, 1.84467e+19, 0, 0, 1.84467e+19, 0, 1.84467e+19,
611 0, 0, 1.84467e+19, 0, 4.975e+11, 0, 0, 4.975e+11, 0, 0, 4.975e+11, 0, 0, 4.975e+11, 0, 1, 4.975e+11, 0, 0,
612 4.975e+11, 0, 0, 4.975e+11, 0, 0, 4.975e+11, 0, 0, 4.975e+11, 0, 0, 4.975e+11, 0, 0, 4.975e+11, 0, 0, 4.975e+11,
613 0, 0, 4.975e+11, 0, 0, 4.975e+11, 0, 0, 4.975e+11, 0, 0, 4.975e+11, 0, 0, 1.256e+09, 0, 0, 3.504e+09, 0, 0,
614 3.056e+09, 0, 0, 3.1e+09, 0, 0, 2.952e+09, 0, 0, 2.404e+09, 0, 0, 2.304e+09, 0, 0, 2.556e+09, 0, 0, 2.4e+09, 0,
615 0, 2.804e+09, 0, 0, 2.552e+09, 0, 0, 2.408e+09, 0, 0, 2.9e+09, 0, 0, 2.4e+09, 0, 0, 2.256e+09, 0, 0, 3.504e+09,
616 0, 0, 3.244e+09, 0, 0, 2.556e+09, 0, 0, 2.952e+09, 0, 0, 1.26e+09, 0, 0, 2.552e+09, 0, 0, 1.4e+09, 0, 0,
617 3.244e+09, 0, 0, 8.52e+08, 0, 0, 2.556e+09, 0, 0, 1.756e+09, 0, 0, 2.256e+09, 0, 0, 3.4e+09, 0, 0, 2.6e+09, 0,
618 0, 1.352e+09, 0, 0, 2.952e+09, 0, 0, 1.2e+09, 0, 0, 2.452e+09, 0, 0, 9.48e+08, 0, 0, 2.804e+09, 0, 0, 1.4e+09,
619 0, 0, 3.1e+09, 0, 0, 1.452e+09, 0, 0, 2.404e+09, 0, 0, 1.3e+09, 0, 0, 2.952e+09, 0, 0, 1.2e+09, 0, 0, 3.056e+09,
620 0, 0, 2.804e+09, 0, 0, 2.4e+09, 0, 0, 8.56001e+08, 0, 0, 2.9e+09, 0, 0, 1.5e+09, 0, 0;
622 Eigen::VectorXd C(cnsts);
623 C << 3.2e+20, 3.2e+20, 2.5e+09, 2.5e+09, 2.5e+09, 2.5e+09, 2.5e+09, 3.2e+20, 3.2e+20, 3.2e+20, 3.2e+20, 3.2e+20,
624 3.2e+20, 3.2e+20, 3.2e+20, 3.2e+20, 3.2e+20, 3.2e+20, 3.2e+20, 3.2e+20, 3.2e+20, 3.2e+20, 3.2e+20, 1.83484e+10,
625 2.5e+09, 2.5e+09, 2.5e+09, 2.5e+09, 2.5e+09, 2.5e+09, 2.5e+09, 2.5e+09, 2.5e+09, 2.5e+09, 2.5e+09, 2.5e+09,
626 2.5e+09, 2.5e+09, 2.5e+09, 2.5e+09, 2.5e+09, 2.5e+09, 1.83484e+10, 2.5e+09, 1.83484e+10, 2.5e+09, 1.83484e+10,
627 2.5e+09, 1.83484e+10, 2.5e+09, 1.83484e+10, 2.5e+09, 1.83484e+10, 2.5e+09, 1.83484e+10, 2.5e+09, 1.83484e+10,
628 2.5e+09, 1.83484e+10, 2.5e+09, 1.83484e+10, 2.5e+09, 1.83484e+10, 2.5e+09, 1.83484e+10, 2.5e+09, 1.83484e+10,
629 2.5e+09, 1.83484e+10, 2.5e+09, 1.83484e+10;
631 Eigen::VectorXd phi(flows);
632 phi << 1.35273, 2.27328e-10, 2.27328e-10;
634 std::vector<bool> shared(cnsts, true);
635 lmm::BmfSolver solver(A, maxA, C, shared, phi);
636 auto rho = solver.solve();
637 REQUIRE((rho.array() > 0).all());
640 SECTION("is_bmf bug: all limited by bound")
643 * Particular case, 1 flow is saturated and the other increases
644 * its speed until the contraint is reached
647 lmm::Constraint* sys_cnst = Sys.constraint_new(nullptr, 10);
648 lmm::Constraint* sys_cnst2 = Sys.constraint_new(nullptr, 8);
649 lmm::Variable* rho_1 = Sys.variable_new(nullptr, 1, 1.5, 2);
650 lmm::Variable* rho_2 = Sys.variable_new(nullptr, 1, 3, 2);
652 Sys.expand(sys_cnst, rho_1, 5.0);
653 Sys.expand(sys_cnst2, rho_1, 1.0);
654 Sys.expand(sys_cnst, rho_2, 1.0);
655 Sys.expand(sys_cnst2, rho_2, 1.0);
657 REQUIRE(double_equals(rho_1->get_value(), 1.4, sg_precision_workamount));
658 REQUIRE(double_equals(rho_2->get_value(), 3, sg_precision_workamount));
661 SECTION("s4u-cloud-capping bug: all limited by bound extra case")
664 * Inspired by s4u-cloud-capping test.
665 * Step 9: "Put an activity on a PM and an activity on the VM capped by 10%."
666 * Extracted from original test.
667 * The sum of bounds for 1 resource is smaller than C.
670 lmm::Constraint* sys_cnst = Sys.constraint_new(nullptr, 7.6296e+07);
671 lmm::Variable* rho_1 = Sys.variable_new(nullptr, 1, 7.6296e+06, 1);
672 lmm::Variable* rho_2 = Sys.variable_new(nullptr, 1, 3.8148e+07, 1);
674 Sys.expand(sys_cnst, rho_1, 1);
675 Sys.expand(sys_cnst, rho_2, 1);
677 REQUIRE(double_equals(rho_1->get_value(), 7.6296e+06, sg_precision_workamount));
678 REQUIRE(double_equals(rho_2->get_value(), 3.8148e+07, sg_precision_workamount));
681 SECTION("Variable penalty with bounds: thread bug")
684 * Detected by exec-thread.
685 * Fair-sharing vector depends on the penalty too.
688 /* don't change the order of creation, important to trigger the bug */
689 lmm::Constraint* sys_cnst = Sys.constraint_new(nullptr, 4e8);
690 lmm::Variable* rho_2 = Sys.variable_new(nullptr, .25, 4e8, 1);
691 lmm::Variable* rho_1 = Sys.variable_new(nullptr, 1, 1e8, 1);
692 Sys.expand(sys_cnst, rho_2, 1);
693 Sys.expand(sys_cnst, rho_1, 1);
696 REQUIRE(double_equals(rho_1->get_value(), 8e7, sg_precision_workamount));
697 REQUIRE(double_equals(rho_2->get_value(), 3.2e8, sg_precision_workamount));
700 SECTION("Variable penalty with bounds greater than C")
703 * Detected by exec-thread. 6 thread running in a 4 core CPU.
706 lmm::Constraint* sys_cnst = Sys.constraint_new(nullptr, 4e8);
707 lmm::Variable* rho_1 = Sys.variable_new(nullptr, 1.0 / 6.0, 6e8, 1);
708 Sys.expand(sys_cnst, rho_1, 1);
711 REQUIRE(double_equals(rho_1->get_value(), 4e8, sg_precision_workamount));
714 Sys.variable_free_all();
717 TEST_CASE("kernel::bmf Stress-tests", "[.kernel-bmf-stress]")
719 lmm::BmfSystem Sys(false);
721 auto create_cnsts = [&Sys](int C, int capacity) -> auto
723 std::vector<lmm::Constraint*> sys_cnst;
724 for (int i = 0; i < C; i++) {
725 sys_cnst.push_back(Sys.constraint_new(nullptr, capacity));
730 auto test_shared = [&Sys, &create_cnsts](int C, int N, int capacity, const auto& data) {
731 auto sys_cnst = create_cnsts(C, capacity);
732 for (int j = 0; j < N; j++) {
733 lmm::Variable* rho = Sys.variable_new(nullptr, 1, -1, C);
734 for (int i = 0; i < C; i++) {
735 Sys.expand(sys_cnst[i], rho, data[i * j + j]);
741 SECTION("Random consumptions - independent flows")
745 auto data = GENERATE_COPY(chunk(C * N, take(100000, random(0., 1.0))));
746 auto sys_cnst = create_cnsts(C, 1);
747 for (int j = 0; j < N; j++) {
748 for (int i = 0; i < C; i++) {
749 lmm::Variable* rho = Sys.variable_new(nullptr, 1);
750 Sys.expand(sys_cnst[i], rho, data[i * j + j]);
756 SECTION("Random consumptions - flows sharing resources")
760 auto data = GENERATE_COPY(chunk(C * N, take(100000, random(0., 1.0))));
761 test_shared(C, N, 1, data);
764 SECTION("Random integer consumptions - flows sharing resources")
768 auto data = GENERATE_COPY(chunk(C * N, take(100000, random(1, 10))));
769 test_shared(C, N, 10, data);
772 SECTION("Random consumptions - high number of constraints")
776 auto data = GENERATE_COPY(chunk(C * N, take(100000, random(0., 1.0))));
777 test_shared(C, N, 1, data);
780 SECTION("Random integer consumptions - high number of constraints")
784 auto data = GENERATE_COPY(chunk(C * N, take(100000, random(1, 10))));
785 test_shared(C, N, 10, data);
788 Sys.variable_free_all();
791 TEST_CASE("kernel::AllocationGenerator Basic tests", "[kernel-bmf-allocation-gen]")
793 SECTION("Full combinations")
795 Eigen::MatrixXd A(3, 3);
796 A << 1, .5, 1, 1, 1, .5, 1, .75, .75;
797 lmm::AllocationGenerator gen(std::move(A));
799 std::vector<int> alloc;
800 while (gen.next(alloc))
802 REQUIRE(i == 3 * 3 * 3);
805 SECTION("Few options per player")
807 Eigen::MatrixXd A(3, 3);
808 A << 1, 0, 0, 0, 1, 0, 0, 1, 1;
809 lmm::AllocationGenerator gen(std::move(A));
811 std::vector<int> alloc;
812 while (gen.next(alloc))
814 REQUIRE(i == 1 * 2 * 1);