# 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:
    00014   void SetDemands(LinkGraphJob &job, NodeID from, NodeID to, uint demand_forw);
    00015 };
    00016
    00020 class SymmetricScaler : public Scaler {
    00021 public:
    00027   inline SymmetricScaler(uint mod_size) : mod_size(mod_size), supply_sum(0),
    00028     demand_per_node(0)
    00029   {}
    00030
    00035   inline void AddNode(const Node &node)
    00036   {
    00037     this->supply_sum += node.Supply();
    00038   }
    00039
    00044   inline void SetDemandPerNode(uint num_demands)
    00045   {
    00046     this->demand_per_node = max(this->supply_sum / num_demands, 1U);
    00047   }
    00048
    00056   inline uint EffectiveSupply(const Node &from, const Node &to)
    00057   {
    00058     return max(from.Supply() * max(1U, to.Supply()) * this->mod_size / 100 / this->demand_per_node, 1U);
    00059   }
    00060
    00068   inline bool HasDemandLeft(const Node &to)
    00069   {
    00070     return (to.Supply() == 0 || to.UndeliveredSupply() > 0) && to.Demand() > 0;
    00071   }
    00072
    00073   void SetDemands(LinkGraphJob &job, NodeID from, NodeID to, uint demand_forw);
    00074
    00075 private:
    00076   uint mod_size;
    00077   uint supply_sum;
    00078   uint demand_per_node;
    00079 };
    00080
    00084 class AsymmetricScaler : public Scaler {
    00085 public:
    00090   inline void AddNode(const Node &)
    00091   {
    00092   }
    00093
    00098   inline void SetDemandPerNode(uint)
    00099   {
    00100   }
    00101
    00107   inline uint EffectiveSupply(const Node &from, const Node &)
    00108   {
    00109     return from.Supply();
    00110   }
    00111
    00118   inline bool HasDemandLeft(const Node &to) { return to.Demand() > 0; }
    00119 };
    00120
    00129 void SymmetricScaler::SetDemands(LinkGraphJob &job, NodeID from_id, NodeID to_id, uint demand_forw)
    00130 {
    00131   if (job[from_id].Demand() > 0) {
    00132     uint demand_back = demand_forw * this->mod_size / 100;
    00133     uint undelivered = job[to_id].UndeliveredSupply();
    00134     if (demand_back > undelivered) {
    00135       demand_back = undelivered;
    00136       demand_forw = max(1U, demand_back * 100 / this->mod_size);
    00137     }
    00138     this->Scaler::SetDemands(job, to_id, from_id, demand_back);
    00139   }
    00140
    00141   this->Scaler::SetDemands(job, from_id, to_id, demand_forw);
    00142 }
    00143
    00152 inline void Scaler::SetDemands(LinkGraphJob &job, NodeID from_id, NodeID to_id, uint demand_forw)
    00153 {
    00154   job[from_id].DeliverSupply(to_id, demand_forw);
    00155 }
    00156
    00162 template<class Tscaler>
    00163 void DemandCalculator::CalcDemand(LinkGraphJob &job, Tscaler scaler)
    00164 {
    00165   NodeList supplies;
    00166   NodeList demands;
    00167   uint num_supplies = 0;
    00168   uint num_demands = 0;
    00169
    00170   for (NodeID node = 0; node < job.Size(); node++) {
    00171     scaler.AddNode(job[node]);
    00172     if (job[node].Supply() > 0) {
    00173       supplies.push_back(node);
    00174       num_supplies++;
    00175     }
    00176     if (job[node].Demand() > 0) {
    00177       demands.push_back(node);
    00178       num_demands++;
    00179     }
    00180   }
    00181
    00182   if (num_supplies == 0 || num_demands == 0) return;
    00183
    00184   /* Mean acceptance attributed to each node. If the distribution is
    00185    * symmetric this is relative to remote supply, otherwise it is
    00186    * relative to remote demand. */
    00187   scaler.SetDemandPerNode(num_demands);
    00188   uint chance = 0;
    00189
    00190   while (!supplies.empty() && !demands.empty()) {
    00191     NodeID from_id = supplies.front();
    00192     supplies.pop_front();
    00193
    00194     for (uint i = 0; i < num_demands; ++i) {
    00195       assert(!demands.empty());
    00196       NodeID to_id = demands.front();
    00197       demands.pop_front();
    00198       if (from_id == to_id) {
    00199         /* Only one node with supply and demand left */
    00200         if (demands.empty() && supplies.empty()) return;
    00201
    00202         demands.push_back(to_id);
    00203         continue;
    00204       }
    00205
    00206       int32 supply = scaler.EffectiveSupply(job[from_id], job[to_id]);
    00207       assert(supply > 0);
    00208
    00209       /* Scale the distance by mod_dist around max_distance */
    00210       int32 distance = this->max_distance - (this->max_distance -
    00211           (int32)job[from_id][to_id].Distance()) * this->mod_dist / 100;
    00212
    00213       /* Scale the accuracy by distance around accuracy / 2 */
    00214       int32 divisor = this->accuracy * (this->mod_dist - 50) / 100 +
    00215           this->accuracy * distance / this->max_distance + 1;
    00216
    00217       assert(divisor > 0);
    00218
    00219       uint demand_forw = 0;
    00220       if (divisor <= supply) {
    00221         /* At first only distribute demand if
    00222          * effective supply / accuracy divisor >= 1
    00223          * Others are too small or too far away to be considered. */
    00224         demand_forw = supply / divisor;
    00225       } else if (++chance > this->accuracy * num_demands * num_supplies) {
    00226         /* After some trying, if there is still supply left, distribute
    00227          * demand also to other nodes. */
    00228         demand_forw = 1;
    00229       }
    00230
    00231       demand_forw = min(demand_forw, job[from_id].UndeliveredSupply());
    00232
    00233       scaler.SetDemands(job, from_id, to_id, demand_forw);
    00234
    00235       if (scaler.HasDemandLeft(job[to_id])) {
    00236         demands.push_back(to_id);
    00237       } else {
    00238         num_demands--;
    00239       }
    00240
    00241       if (job[from_id].UndeliveredSupply() == 0) break;
    00242     }
    00243
    00244     if (job[from_id].UndeliveredSupply() != 0) {
    00245       supplies.push_back(from_id);
    00246     } else {
    00247       num_supplies--;
    00248     }
    00249   }
    00250 }
    00251
    00256 DemandCalculator::DemandCalculator(LinkGraphJob &job) :
    00257   max_distance(DistanceMaxPlusManhattan(TileXY(0,0), TileXY(MapMaxX(), MapMaxY())))
    00258 {
    00259   const LinkGraphSettings &settings = job.Settings();
    00260   CargoID cargo = job.Cargo();
    00261
    00262   this->accuracy = settings.accuracy;
    00263   this->mod_dist = settings.demand_distance;
    00264   if (this->mod_dist > 100) {
    00265     /* Increase effect of mod_dist > 100 */
    00266     int over100 = this->mod_dist - 100;
    00267     this->mod_dist = 100 + over100 * over100;
    00268   }
    00269
    00270   switch (settings.GetDistributionType(cargo)) {
    00271     case DT_SYMMETRIC:
    00272       this->CalcDemand<SymmetricScaler>(job, SymmetricScaler(settings.demand_size));
    00273       break;
    00274     case DT_ASYMMETRIC:
    00275       this->CalcDemand<AsymmetricScaler>(job, AsymmetricScaler());
    00276       break;
    00277     default:
    00278       /* Nothing to do. */
    00279       break;
    00280   }
    00281 }