From fcd07352208c76c3cf050718dac314807eaa7105 Mon Sep 17 00:00:00 2001 From: Bruno Donassolo Date: Wed, 23 Feb 2022 10:59:35 +0100 Subject: [PATCH] Support for bounded actions in BMF solver --- src/kernel/lmm/bmf.cpp | 56 +++++++++++++++++++++++++++++-------- src/kernel/lmm/bmf.hpp | 4 ++- src/kernel/lmm/bmf_test.cpp | 26 +++++++++++++++++ 3 files changed, 74 insertions(+), 12 deletions(-) diff --git a/src/kernel/lmm/bmf.cpp b/src/kernel/lmm/bmf.cpp index 91ca26e2d7..5c3c9bec4e 100644 --- a/src/kernel/lmm/bmf.cpp +++ b/src/kernel/lmm/bmf.cpp @@ -54,18 +54,19 @@ void simgrid::kernel::lmm::BmfSystem::set_vector_C() } std::unordered_map> -simgrid::kernel::lmm::BmfSystem::get_alloc(const Eigen::VectorXd& fair_sharing) const +simgrid::kernel::lmm::BmfSystem::get_alloc(const Eigen::VectorXd& fair_sharing, bool initial) const { std::unordered_map> alloc; for (int player_idx = 0; player_idx < A_.cols(); player_idx++) { - int selected_resource = -1; - double min_share = 0; + int selected_resource = NO_RESOURCE; + double bound = idx2Var_.at(player_idx)->get_bound(); + double min_share = (bound <= 0 || initial) ? -1 : bound; for (int cnst_idx = 0; cnst_idx < A_.rows(); cnst_idx++) { if (A_(cnst_idx, player_idx) <= 0.0) continue; double share = fair_sharing[cnst_idx] / A_(cnst_idx, player_idx); - if (selected_resource == -1 || double_positive(min_share - share, sg_maxmin_precision)) { + if (min_share == -1 || double_positive(min_share - share, sg_maxmin_precision)) { selected_resource = cnst_idx; min_share = share; } @@ -122,6 +123,16 @@ std::string simgrid::kernel::lmm::BmfSystem::debug_alloc(const std::unordered_ma return debug.str(); } +double simgrid::kernel::lmm::BmfSystem::get_resource_capacity(int resource, + const std::vector& bounded_players) const +{ + double capacity = C_[resource]; + for (int p : bounded_players) { + capacity -= A_(resource, p) * idx2Var_.at(p)->get_bound(); + } + return capacity; +} + Eigen::VectorXd simgrid::kernel::lmm::BmfSystem::equilibrium(const std::unordered_map>& alloc) const { @@ -133,11 +144,16 @@ simgrid::kernel::lmm::BmfSystem::equilibrium(const std::unordered_map bounded_players; for (const auto& e : alloc) { // add one row for the resource with A[r,] int cur_resource = e.first; + if (cur_resource == NO_RESOURCE) { + bounded_players.insert(bounded_players.end(), e.second.begin(), e.second.end()); + continue; + } A_p.row(first_row) = A_.row(cur_resource); - C_p[first_row] = C_[cur_resource]; + C_p[first_row] = get_resource_capacity(cur_resource, bounded_players); first_row++; if (e.second.size() > 1) { int i = e.second[0]; // first player @@ -151,11 +167,19 @@ simgrid::kernel::lmm::BmfSystem::equilibrium(const std::unordered_map(A_p).solve(C_p); + Eigen::VectorXd rho = Eigen::FullPivLU(A_p).solve(C_p); + for (int p : bounded_players) { + rho[p] = idx2Var_.at(p)->get_bound(); + } + return rho; } bool simgrid::kernel::lmm::BmfSystem::is_bmf(const Eigen::VectorXd& rho) const @@ -167,9 +191,6 @@ bool simgrid::kernel::lmm::BmfSystem::is_bmf(const Eigen::VectorXd& rho) const bmf = bmf && (not std::any_of(remaining.data(), remaining.data() + remaining.size(), [](double v) { return double_positive(v, sg_maxmin_precision); })); - // 2) at least 1 resource is saturated - bmf = bmf && (std::any_of(remaining.data(), remaining.data() + remaining.size(), - [](double v) { return double_equals(v, 0.0, sg_maxmin_precision); })); // 3) every player receives maximum share in at least 1 saturated resource // due to subflows, compare with the maximum consumption and not the A matrix Eigen::MatrixXd usage = @@ -186,6 +207,17 @@ bool simgrid::kernel::lmm::BmfSystem::is_bmf(const Eigen::VectorXd& rho) const XBT_DEBUG("Saturated_j resources:\n%s", debug_eigen(saturated).c_str()); player_max_share.array().colwise() *= saturated.array(); + // just check if it has received at least it's bound + for (int p = 0; p < rho.size(); p++) { + if (double_equals(rho[p], idx2Var_.at(p)->get_bound(), sg_maxmin_precision)) { + player_max_share(0, p) = 1; // it doesn't really matter, just to say that it's a bmf + saturated[0] = 1; + } + } + + // 2) at least 1 resource is saturated + bmf = bmf && (saturated.array() == 1).any(); + XBT_DEBUG("Player_ji usage of saturated resources:\n%s", debug_eigen(player_max_share).c_str()); // for all columns(players) it has to be the max at least in 1 bmf = bmf && (player_max_share.colwise().sum().all() >= 1); @@ -197,6 +229,8 @@ void simgrid::kernel::lmm::BmfSystem::bottleneck_solve() if (not modified_) return; + XBT_DEBUG(""); + XBT_DEBUG("Starting BMF solver"); /* initialize players' weight and constraint matrices */ set_vector_C(); set_matrix_A(); @@ -212,7 +246,7 @@ void simgrid::kernel::lmm::BmfSystem::bottleneck_solve() /* BMF allocation for each player (current and last one) stop when are equal */ std::unordered_map> last_alloc; - auto cur_alloc = get_alloc(fair_sharing); + auto cur_alloc = get_alloc(fair_sharing, true); Eigen::VectorXd rho; while (it < max_iteration_ && last_alloc != cur_alloc) { last_alloc = cur_alloc; @@ -228,7 +262,7 @@ void simgrid::kernel::lmm::BmfSystem::bottleneck_solve() XBT_DEBUG("Fair sharing vector (per resource):\n%s", debug_eigen(fair_sharing).c_str()); // get new allocation for players - cur_alloc = get_alloc(fair_sharing); + cur_alloc = get_alloc(fair_sharing, false); XBT_DEBUG("B (new allocation): %s", debug_alloc(cur_alloc).c_str()); it++; } diff --git a/src/kernel/lmm/bmf.hpp b/src/kernel/lmm/bmf.hpp index 13b42cc763..b05fe298a1 100644 --- a/src/kernel/lmm/bmf.hpp +++ b/src/kernel/lmm/bmf.hpp @@ -22,7 +22,8 @@ private: void bottleneck_solve(); void set_matrix_A(); void set_vector_C(); - std::unordered_map> get_alloc(const Eigen::VectorXd& fair_sharing) const; + std::unordered_map> get_alloc(const Eigen::VectorXd& fair_sharing, bool initial) const; + double get_resource_capacity(int resource, const std::vector& bounded_players) const; Eigen::VectorXd equilibrium(const std::unordered_map>& alloc) const; void set_fair_sharing(const std::unordered_map>& alloc, const Eigen::VectorXd& rho, @@ -49,6 +50,7 @@ private: std::unordered_map idx2Var_; Eigen::VectorXd C_; std::unordered_map cnst2idx_; + static constexpr int NO_RESOURCE = -1; //!< flag to indicate player has selected no resource }; } // namespace lmm diff --git a/src/kernel/lmm/bmf_test.cpp b/src/kernel/lmm/bmf_test.cpp index ef11a691cd..2665e8d08a 100644 --- a/src/kernel/lmm/bmf_test.cpp +++ b/src/kernel/lmm/bmf_test.cpp @@ -115,6 +115,32 @@ TEST_CASE("kernel::bmf Basic tests", "[kernel-bmf-basic]") REQUIRE(double_positive(rho_1->get_value(), sg_maxmin_precision)); } + SECTION("Bounded variable") + { + /* + * Assures a player receives the min(bound, share) if it's bounded + * + * o System: a1 * p1 * \rho1 + a2 * p2 * \rho2 < C + * o bounds: b1=0.1, b2=-1 + * o consumption_weight: a1=1, a2=1 + * o sharing_penalty: p1=1, p2=1 + * + * Expectations + * o rho1 = .1 + * o rho2 = .8 + */ + + lmm::Constraint* sys_cnst = Sys.constraint_new(nullptr, 1); + lmm::Variable* rho_1 = Sys.variable_new(nullptr, 1, .1); + lmm::Variable* rho_2 = Sys.variable_new(nullptr, 1); + + Sys.expand(sys_cnst, rho_1, 2); + Sys.expand(sys_cnst, rho_2, 1); + Sys.solve(); + REQUIRE(double_equals(rho_1->get_value(), .1, sg_maxmin_precision)); + REQUIRE(double_equals(rho_2->get_value(), .8, sg_maxmin_precision)); + } + Sys.variable_free_all(); } -- 2.20.1