# demands.cpp

Go to the documentation of this file.

    00001
    00003 #include "../stdafx.h"
    00004 #include "demands.h"
    00005 #include <list>
    00006
    00007 typedef std::list<NodeID> NodeList;
    00008
    00012 class Scaler {
    00013 public:
    00017   Scaler() : demand_per_node(0) {}
    00018
    00019   void SetDemands(LinkGraphJob &job, NodeID from, NodeID to, uint demand_forw);
    00020
    00021 protected:
    00022   uint demand_per_node;
    00023 };
    00024
    00028 class SymmetricScaler : public Scaler {
    00029 public:
    00035   inline SymmetricScaler(uint mod_size) : mod_size(mod_size), supply_sum(0)
    00036   {}
    00037
    00042   inline void AddNode(const Node &node)
    00043   {
    00044     this->supply_sum += node.Supply();
    00045   }
    00046
    00051   inline void SetDemandPerNode(uint num_demands)
    00052   {
    00053     this->demand_per_node = max(this->supply_sum / num_demands, 1U);
    00054   }
    00055
    00063   inline uint EffectiveSupply(const Node &from, const Node &to)
    00064   {
    00065     return max(from.Supply() * max(1U, to.Supply()) * this->mod_size / 100 / this->demand_per_node, 1U);
    00066   }
    00067
    00075   inline bool HasDemandLeft(const Node &to)
    00076   {
    00077     return (to.Supply() == 0 || to.UndeliveredSupply() > 0) && to.Demand() > 0;
    00078   }
    00079
    00080   void SetDemands(LinkGraphJob &job, NodeID from, NodeID to, uint demand_forw);
    00081
    00082 private:
    00083   uint mod_size;
    00084   uint supply_sum;
    00085 };
    00086
    00090 class AsymmetricScaler : public Scaler {
    00091 public:
    00095   inline AsymmetricScaler() : demand_sum(0) {}
    00096
    00101   inline void AddNode(const Node &node)
    00102   {
    00103     this->demand_sum += node.Demand();
    00104   }
    00105
    00110   inline void SetDemandPerNode(uint num_demands)
    00111   {
    00112     this->demand_per_node = max(this->demand_sum / num_demands, (uint)1);
    00113   }
    00114
    00121   inline uint EffectiveSupply(const Node &from, const Node &to)
    00122   {
    00123     return max(from.Supply() * to.Demand() / this->demand_per_node, (uint)1);
    00124   }
    00125
    00132   inline bool HasDemandLeft(const Node &to) { return to.Demand() > 0; }
    00133
    00134 private:
    00135   uint demand_sum;
    00136 };
    00137
    00146 void SymmetricScaler::SetDemands(LinkGraphJob &job, NodeID from_id, NodeID to_id, uint demand_forw)
    00147 {
    00148   if (job[from_id].Demand() > 0) {
    00149     uint demand_back = demand_forw * this->mod_size / 100;
    00150     uint undelivered = job[to_id].UndeliveredSupply();
    00151     if (demand_back > undelivered) {
    00152       demand_back = undelivered;
    00153       demand_forw = max(1U, demand_back * 100 / this->mod_size);
    00154     }
    00155     this->Scaler::SetDemands(job, to_id, from_id, demand_back);
    00156   }
    00157
    00158   this->Scaler::SetDemands(job, from_id, to_id, demand_forw);
    00159 }
    00160
    00169 inline void Scaler::SetDemands(LinkGraphJob &job, NodeID from_id, NodeID to_id, uint demand_forw)
    00170 {
    00171   job[from_id].DeliverSupply(to_id, demand_forw);
    00172 }
    00173
    00179 template<class Tscaler>
    00180 void DemandCalculator::CalcDemand(LinkGraphJob &job, Tscaler scaler)
    00181 {
    00182   NodeList supplies;
    00183   NodeList demands;
    00184   uint num_supplies = 0;
    00185   uint num_demands = 0;
    00186
    00187   for (NodeID node = 0; node < job.Size(); node++) {
    00188     scaler.AddNode(job[node]);
    00189     if (job[node].Supply() > 0) {
    00190       supplies.push_back(node);
    00191       num_supplies++;
    00192     }
    00193     if (job[node].Demand() > 0) {
    00194       demands.push_back(node);
    00195       num_demands++;
    00196     }
    00197   }
    00198
    00199   if (num_supplies == 0 || num_demands == 0) return;
    00200
    00201   /* Mean acceptance attributed to each node. If the distribution is
    00202    * symmetric this is relative to remote supply, otherwise it is
    00203    * relative to remote demand. */
    00204   scaler.SetDemandPerNode(num_demands);
    00205   uint chance = 0;
    00206
    00207   while (!supplies.empty() && !demands.empty()) {
    00208     NodeID from_id = supplies.front();
    00209     supplies.pop_front();
    00210
    00211     for (uint i = 0; i < num_demands; ++i) {
    00212       assert(!demands.empty());
    00213       NodeID to_id = demands.front();
    00214       demands.pop_front();
    00215       if (from_id == to_id) {
    00216         /* Only one node with supply and demand left */
    00217         if (demands.empty() && supplies.empty()) return;
    00218
    00219         demands.push_back(to_id);
    00220         continue;
    00221       }
    00222
    00223       int32 supply = scaler.EffectiveSupply(job[from_id], job[to_id]);
    00224       assert(supply > 0);
    00225
    00226       /* Scale the distance by mod_dist around max_distance */
    00227       int32 distance = this->max_distance - (this->max_distance -
    00228           (int32)job[from_id][to_id].Distance()) * this->mod_dist / 100;
    00229
    00230       /* Scale the accuracy by distance around accuracy / 2 */
    00231       int32 divisor = this->accuracy * (this->mod_dist - 50) / 100 +
    00232           this->accuracy * distance / this->max_distance + 1;
    00233
    00234       assert(divisor > 0);
    00235
    00236       uint demand_forw = 0;
    00237       if (divisor <= supply) {
    00238         /* At first only distribute demand if
    00239          * effective supply / accuracy divisor >= 1
    00240          * Others are too small or too far away to be considered. */
    00241         demand_forw = supply / divisor;
    00242       } else if (++chance > this->accuracy * num_demands * num_supplies) {
    00243         /* After some trying, if there is still supply left, distribute
    00244          * demand also to other nodes. */
    00245         demand_forw = 1;
    00246       }
    00247
    00248       demand_forw = min(demand_forw, job[from_id].UndeliveredSupply());
    00249
    00250       scaler.SetDemands(job, from_id, to_id, demand_forw);
    00251
    00252       if (scaler.HasDemandLeft(job[to_id])) {
    00253         demands.push_back(to_id);
    00254       } else {
    00255         num_demands--;
    00256       }
    00257
    00258       if (job[from_id].UndeliveredSupply() == 0) break;
    00259     }
    00260
    00261     if (job[from_id].UndeliveredSupply() != 0) {
    00262       supplies.push_back(from_id);
    00263     } else {
    00264       num_supplies--;
    00265     }
    00266   }
    00267 }
    00268
    00273 DemandCalculator::DemandCalculator(LinkGraphJob &job) :
    00274   max_distance(MapSizeX() + MapSizeY() - 2)
    00275 {
    00276   const LinkGraphSettings &settings = job.Settings();
    00277   CargoID cargo = job.Cargo();
    00278
    00279   this->accuracy = settings.accuracy;
    00280   this->mod_dist = settings.demand_distance;
    00281   if (this->mod_dist > 100) {
    00282     /* Increase effect of mod_dist > 100 */
    00283     int over100 = this->mod_dist - 100;
    00284     this->mod_dist = 100 + over100 * over100;
    00285   }
    00286
    00287   switch (settings.GetDistributionType(cargo)) {
    00288     case DT_SYMMETRIC:
    00289       this->CalcDemand<SymmetricScaler>(job, SymmetricScaler(settings.demand_size));
    00290       break;
    00291     case DT_ASYMMETRIC:
    00292       this->CalcDemand<AsymmetricScaler>(job, AsymmetricScaler());
    00293       break;
    00294     default:
    00295       /* Nothing to do. */
    00296       break;
    00297   }
    00298 }