1 /** @file demands.cpp Definition of demand calculating link graph handler. */
5 #include "../core/math_func.hpp"
8 #include "../safeguards.h"
10 typedef std::queue
<NodeID
> NodeList
;
13 * Scale various things according to symmetric/asymmetric distribution.
17 void SetDemands(LinkGraphJob
&job
, NodeID from
, NodeID to
, uint demand_forw
);
21 * Scaler for symmetric distribution.
23 class SymmetricScaler
: public Scaler
{
27 * @param mod_size Size modifier to be used. Determines how much demands
28 * increase with the supply of the remote station.
30 inline SymmetricScaler(uint mod_size
) : mod_size(mod_size
), supply_sum(0),
35 * Count a node's supply into the sum of supplies.
38 inline void AddNode(const Node
&node
)
40 this->supply_sum
+= node
.base
.supply
;
44 * Calculate the mean demand per node using the sum of supplies.
45 * @param num_demands Number of accepting nodes.
47 inline void SetDemandPerNode(uint num_demands
)
49 this->demand_per_node
= std::max(this->supply_sum
/ num_demands
, 1U);
53 * Get the effective supply of one node towards another one. In symmetric
54 * distribution the supply of the other node is weighed in.
55 * @param from The supplying node.
56 * @param to The receiving node.
57 * @return Effective supply.
59 inline uint
EffectiveSupply(const Node
&from
, const Node
&to
)
61 return std::max(from
.base
.supply
* std::max(1U, to
.base
.supply
) * this->mod_size
/ 100 / this->demand_per_node
, 1U);
65 * Check if there is any acceptance left for this node. In symmetric distribution
66 * nodes only accept anything if they also supply something. So if
67 * undelivered_supply == 0 at the node there isn't any demand left either.
68 * @param to Node to be checked.
69 * @return If demand is left.
71 inline bool HasDemandLeft(const Node
&to
)
73 return (to
.base
.supply
== 0 || to
.undelivered_supply
> 0) && to
.base
.demand
> 0;
76 void SetDemands(LinkGraphJob
&job
, NodeID from
, NodeID to
, uint demand_forw
);
79 uint mod_size
; ///< Size modifier. Determines how much demands increase with the supply of the remote station.
80 uint supply_sum
; ///< Sum of all supplies in the component.
81 uint demand_per_node
; ///< Mean demand associated with each node.
85 * A scaler for asymmetric distribution.
87 class AsymmetricScaler
: public Scaler
{
93 inline void AddNode(const Node
&)
101 inline void SetDemandPerNode(uint
)
106 * Get the effective supply of one node towards another one.
107 * @param from The supplying node.
110 inline uint
EffectiveSupply(const Node
&from
, const Node
&)
112 return from
.base
.supply
;
116 * Check if there is any acceptance left for this node. In asymmetric distribution
117 * nodes always accept as long as their demand > 0.
118 * @param to The node to be checked.
120 inline bool HasDemandLeft(const Node
&to
) { return to
.base
.demand
> 0; }
124 * Set the demands between two nodes using the given base demand. In symmetric mode
125 * this sets demands in both directions.
126 * @param job The link graph job.
127 * @param from_id The supplying node.
128 * @param to_id The receiving node.
129 * @param demand_forw Demand calculated for the "forward" direction.
131 void SymmetricScaler::SetDemands(LinkGraphJob
&job
, NodeID from_id
, NodeID to_id
, uint demand_forw
)
133 if (job
[from_id
].base
.demand
> 0) {
134 uint demand_back
= demand_forw
* this->mod_size
/ 100;
135 uint undelivered
= job
[to_id
].undelivered_supply
;
136 if (demand_back
> undelivered
) {
137 demand_back
= undelivered
;
138 demand_forw
= std::max(1U, demand_back
* 100 / this->mod_size
);
140 this->Scaler::SetDemands(job
, to_id
, from_id
, demand_back
);
143 this->Scaler::SetDemands(job
, from_id
, to_id
, demand_forw
);
147 * Set the demands between two nodes using the given base demand. In asymmetric mode
148 * this only sets demand in the "forward" direction.
149 * @param job The link graph job.
150 * @param from_id The supplying node.
151 * @param to_id The receiving node.
152 * @param demand_forw Demand calculated for the "forward" direction.
154 inline void Scaler::SetDemands(LinkGraphJob
&job
, NodeID from_id
, NodeID to_id
, uint demand_forw
)
156 job
[from_id
].DeliverSupply(to_id
, demand_forw
);
160 * Do the actual demand calculation, called from constructor.
161 * @param job Job to calculate the demands for.
162 * @tparam Tscaler Scaler to be used for scaling demands.
164 template <class Tscaler
>
165 void DemandCalculator::CalcDemand(LinkGraphJob
&job
, Tscaler scaler
)
169 uint num_supplies
= 0;
170 uint num_demands
= 0;
172 for (NodeID node
= 0; node
< job
.Size(); node
++) {
173 scaler
.AddNode(job
[node
]);
174 if (job
[node
].base
.supply
> 0) {
178 if (job
[node
].base
.demand
> 0) {
184 if (num_supplies
== 0 || num_demands
== 0) return;
186 /* Mean acceptance attributed to each node. If the distribution is
187 * symmetric this is relative to remote supply, otherwise it is
188 * relative to remote demand. */
189 scaler
.SetDemandPerNode(num_demands
);
192 while (!supplies
.empty() && !demands
.empty()) {
193 NodeID from_id
= supplies
.front();
196 for (uint i
= 0; i
< num_demands
; ++i
) {
197 assert(!demands
.empty());
198 NodeID to_id
= demands
.front();
200 if (from_id
== to_id
) {
201 /* Only one node with supply and demand left */
202 if (demands
.empty() && supplies
.empty()) return;
208 int32_t supply
= scaler
.EffectiveSupply(job
[from_id
], job
[to_id
]);
211 constexpr int32_t divisor_scale
= 16;
213 int32_t scaled_distance
= this->base_distance
;
214 if (this->mod_dist
> 0) {
215 const int32_t distance
= DistanceMaxPlusManhattan(job
[from_id
].base
.xy
, job
[to_id
].base
.xy
);
216 /* Scale distance around base_distance by (mod_dist * (100 / 1024)).
217 * mod_dist may be > 1024, so clamp result to be non-negative */
218 scaled_distance
= std::max(0, this->base_distance
+ (((distance
- this->base_distance
) * this->mod_dist
) / 1024));
221 /* Scale the accuracy by distance around accuracy / 2 */
222 const int32_t divisor
= divisor_scale
+ ((this->accuracy
* scaled_distance
* divisor_scale
) / (this->base_distance
* 2));
223 assert(divisor
>= divisor_scale
);
225 uint demand_forw
= 0;
226 if (divisor
<= (supply
* divisor_scale
)) {
227 /* At first only distribute demand if
228 * effective supply / accuracy divisor >= 1
229 * Others are too small or too far away to be considered. */
230 demand_forw
= (supply
* divisor_scale
) / divisor
;
231 } else if (++chance
> this->accuracy
* num_demands
* num_supplies
) {
232 /* After some trying, if there is still supply left, distribute
233 * demand also to other nodes. */
237 demand_forw
= std::min(demand_forw
, job
[from_id
].undelivered_supply
);
239 scaler
.SetDemands(job
, from_id
, to_id
, demand_forw
);
241 if (scaler
.HasDemandLeft(job
[to_id
])) {
247 if (job
[from_id
].undelivered_supply
== 0) break;
250 if (job
[from_id
].undelivered_supply
!= 0) {
251 supplies
.push(from_id
);
259 * Create the DemandCalculator and immediately do the calculation.
260 * @param job Job to calculate the demands for.
262 DemandCalculator::DemandCalculator(LinkGraphJob
&job
) :
263 base_distance(IntSqrt(DistanceMaxPlusManhattan(TileXY(0,0), TileXY(Map::MaxX(), Map::MaxY()))))
265 const LinkGraphSettings
&settings
= job
.Settings();
266 CargoID cargo
= job
.Cargo();
268 this->accuracy
= settings
.accuracy
;
269 this->mod_dist
= settings
.demand_distance
;
270 if (this->mod_dist
> 100) {
271 /* Increase effect of mod_dist > 100.
278 int over100
= this->mod_dist
- 100;
279 this->mod_dist
= 100 + ((over100
* over100
) / 12);
282 switch (settings
.GetDistributionType(cargo
)) {
284 this->CalcDemand
<SymmetricScaler
>(job
, SymmetricScaler(settings
.demand_size
));
287 this->CalcDemand
<AsymmetricScaler
>(job
, AsymmetricScaler());