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 }