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 }