Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Support for bounded actions in BMF solver
authorBruno Donassolo <bruno.donassolo@inria.fr>
Wed, 23 Feb 2022 09:59:35 +0000 (10:59 +0100)
committerBruno Donassolo <bruno.donassolo@inria.fr>
Mon, 7 Mar 2022 09:23:25 +0000 (10:23 +0100)
src/kernel/lmm/bmf.cpp
src/kernel/lmm/bmf.hpp
src/kernel/lmm/bmf_test.cpp

index 91ca26e..5c3c9be 100644 (file)
@@ -54,18 +54,19 @@ void simgrid::kernel::lmm::BmfSystem::set_vector_C()
 }
 
 std::unordered_map<int, std::vector<int>>
-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<int, std::vector<int>> 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<int>& 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<int, std::vector<int>>& alloc) const
 {
@@ -133,11 +144,16 @@ simgrid::kernel::lmm::BmfSystem::equilibrium(const std::unordered_map<int, std::
   // if so, they must have a fair sharing of this resource, adjust A_p and C_p accordingly
   int last_row = n_players - 1;
   int first_row = 0;
+  std::vector<int> 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<int, std::
       }
     }
   }
+  /* clear players which are externally bounded */
+  for (int p : bounded_players) {
+    A_p.col(p).setZero();
+  }
 
   XBT_DEBUG("A':\n%s", debug_eigen(A_p).c_str());
 
   XBT_DEBUG("C':\n%s", debug_eigen(C_p).c_str());
-  return Eigen::FullPivLU<Eigen::MatrixXd>(A_p).solve(C_p);
+  Eigen::VectorXd rho = Eigen::FullPivLU<Eigen::MatrixXd>(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<int, std::vector<int>> 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++;
   }
index 13b42cc..b05fe29 100644 (file)
@@ -22,7 +22,8 @@ private:
   void bottleneck_solve();
   void set_matrix_A();
   void set_vector_C();
-  std::unordered_map<int, std::vector<int>> get_alloc(const Eigen::VectorXd& fair_sharing) const;
+  std::unordered_map<int, std::vector<int>> get_alloc(const Eigen::VectorXd& fair_sharing, bool initial) const;
+  double get_resource_capacity(int resource, const std::vector<int>& bounded_players) const;
   Eigen::VectorXd equilibrium(const std::unordered_map<int, std::vector<int>>& alloc) const;
 
   void set_fair_sharing(const std::unordered_map<int, std::vector<int>>& alloc, const Eigen::VectorXd& rho,
@@ -49,6 +50,7 @@ private:
   std::unordered_map<int, Variable*> idx2Var_;
   Eigen::VectorXd C_;
   std::unordered_map<const Constraint*, int> cnst2idx_;
+  static constexpr int NO_RESOURCE = -1; //!< flag to indicate player has selected no resource
 };
 
 } // namespace lmm
index ef11a69..2665e8d 100644 (file)
@@ -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();
 }