OpenTTD Source 20260109-master-g241b5fcdfe
mcf.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
10#include "../stdafx.h"
11#include "../core/math_func.hpp"
12#include "../timer/timer_game_tick.h"
13#include "mcf.h"
14
15#include "../safeguards.h"
16
17typedef std::map<NodeID, Path *> PathViaMap;
18
24class DistanceAnnotation : public Path {
25public:
26
32 DistanceAnnotation(NodeID n, bool source = false) : Path(n, source) {}
33
34 bool IsBetter(const DistanceAnnotation *base, uint cap, int free_cap, uint dist) const;
35
40 inline uint GetAnnotation() const { return this->distance; }
41
45 inline void UpdateAnnotation() { }
46
50 struct Comparator {
51 bool operator()(const DistanceAnnotation *x, const DistanceAnnotation *y) const;
52 };
53};
54
61class CapacityAnnotation : public Path {
62 int cached_annotation = 0;
63
64public:
65
71 CapacityAnnotation(NodeID n, bool source = false) : Path(n, source) {}
72
73 bool IsBetter(const CapacityAnnotation *base, uint cap, int free_cap, uint dist) const;
74
79 inline int GetAnnotation() const { return this->cached_annotation; }
80
84 inline void UpdateAnnotation()
85 {
86 this->cached_annotation = this->GetCapacityRatio();
87 }
88
92 struct Comparator {
93 bool operator()(const CapacityAnnotation *x, const CapacityAnnotation *y) const;
94 };
95};
96
102private:
104
105 std::vector<LinkGraphJob::EdgeAnnotation>::const_iterator i;
106 std::vector<LinkGraphJob::EdgeAnnotation>::const_iterator end;
107
108public:
109
115
120 void SetNode(NodeID, NodeID node)
121 {
122 this->i = this->job[node].edges.cbegin();
123 this->end = this->job[node].edges.cend();
124 }
125
130 NodeID Next()
131 {
132 return this->i != this->end ? (this->i++)->base.dest_node : INVALID_NODE;
133 }
134};
135
140private:
142
145
147 FlowStat::SharesMap::const_iterator it;
148
150 FlowStat::SharesMap::const_iterator end;
151public:
152
158 {
159 for (NodeID i = 0; i < job.Size(); ++i) {
160 StationID st = job[i].base.station;
161 if (st >= this->station_to_node.size()) {
162 this->station_to_node.resize(st + 1);
163 }
164 this->station_to_node[st] = i;
165 }
166 }
167
173 void SetNode(NodeID source, NodeID node)
174 {
175 const FlowStatMap &flows = this->job[node].flows;
176 FlowStatMap::const_iterator it = flows.find(this->job[source].base.station);
177 if (it != flows.end()) {
178 this->it = it->second.GetShares()->begin();
179 this->end = it->second.GetShares()->end();
180 } else {
181 this->it = FlowStat::empty_sharesmap.begin();
182 this->end = FlowStat::empty_sharesmap.end();
183 }
184 }
185
190 NodeID Next()
191 {
192 if (this->it == this->end) return INVALID_NODE;
193 return this->station_to_node[(this->it++)->second];
194 }
195};
196
207 int free_cap, uint dist) const
208{
209 /* If any of the paths is disconnected, the other one is better. If both
210 * are disconnected, this path is better.*/
211 if (base->distance == UINT_MAX) {
212 return false;
213 } else if (this->distance == UINT_MAX) {
214 return true;
215 }
216
217 if (free_cap > 0 && base->free_capacity > 0) {
218 /* If both paths have capacity left, compare their distances.
219 * If the other path has capacity left and this one hasn't, the
220 * other one's better (thus, return true). */
221 return this->free_capacity > 0 ? (base->distance + dist < this->distance) : true;
222 } else {
223 /* If the other path doesn't have capacity left, but this one has,
224 * the other one is worse (thus, return false).
225 * If both paths are out of capacity, do the regular distance
226 * comparison. */
227 return this->free_capacity > 0 ? false : (base->distance + dist < this->distance);
228 }
229}
230
241 int free_cap, uint dist) const
242{
243 int min_cap = Path::GetCapacityRatio(std::min(base->free_capacity, free_cap), std::min(base->capacity, cap));
244 int this_cap = this->GetCapacityRatio();
245 if (min_cap == this_cap) {
246 /* If the capacities are the same and the other path isn't disconnected
247 * choose the shorter path. */
248 return base->distance == UINT_MAX ? false : (base->distance + dist < this->distance);
249 } else {
250 return min_cap > this_cap;
251 }
252}
253
263template <class Tannotation, class Tedge_iterator>
264void MultiCommodityFlow::Dijkstra(NodeID source_node, PathVector &paths)
265{
266 typedef std::set<Tannotation *, typename Tannotation::Comparator> AnnoSet;
267 Tedge_iterator iter(this->job);
268 uint16_t size = this->job.Size();
269 AnnoSet annos;
270 paths.resize(size, nullptr);
271 for (NodeID node = 0; node < size; ++node) {
272 Tannotation *anno = new Tannotation(node, node == source_node);
273 anno->UpdateAnnotation();
274 annos.insert(anno);
275 paths[node] = anno;
276 }
277 while (!annos.empty()) {
278 typename AnnoSet::iterator i = annos.begin();
279 Tannotation *source = *i;
280 annos.erase(i);
281 NodeID from = source->GetNode();
282 iter.SetNode(source_node, from);
283 for (NodeID to = iter.Next(); to != INVALID_NODE; to = iter.Next()) {
284 if (to == from) continue; // Not a real edge but a consumption sign.
285 const Edge &edge = this->job[from][to];
286 uint capacity = edge.base.capacity;
287 if (this->max_saturation != UINT_MAX) {
288 capacity *= this->max_saturation;
289 capacity /= 100;
290 if (capacity == 0) capacity = 1;
291 }
292 /* Prioritize the fastest route for passengers, mail and express cargo,
293 * and the shortest route for other classes of cargo.
294 * In-between stops are punished with a 1 tile or 1 day penalty. */
295 bool express = IsCargoInClass(this->job.Cargo(), CargoClass::Passengers) ||
298 uint distance = DistanceMaxPlusManhattan(this->job[from].base.xy, this->job[to].base.xy) + 1;
299 /* Compute a default travel time from the distance and an average speed of 1 tile/day. */
300 uint time = (edge.base.TravelTime() != 0) ? edge.base.TravelTime() + Ticks::DAY_TICKS : distance * Ticks::DAY_TICKS;
301 uint distance_anno = express ? time : distance;
302
303 Tannotation *dest = static_cast<Tannotation *>(paths[to]);
304 if (dest->IsBetter(source, capacity, capacity - edge.Flow(), distance_anno)) {
305 annos.erase(dest);
306 dest->Fork(source, capacity, capacity - edge.Flow(), distance_anno);
307 dest->UpdateAnnotation();
308 annos.insert(dest);
309 }
310 }
311 }
312}
313
319void MultiCommodityFlow::CleanupPaths(NodeID source_id, PathVector &paths)
320{
321 Path *source = paths[source_id];
322 paths[source_id] = nullptr;
323 for (PathVector::iterator i = paths.begin(); i != paths.end(); ++i) {
324 Path *path = *i;
325 if (path == nullptr) continue;
326 if (path->GetParent() == source) path->Detach();
327 while (path != source && path != nullptr && path->GetFlow() == 0) {
328 Path *parent = path->GetParent();
329 path->Detach();
330 if (path->GetNumChildren() == 0) {
331 paths[path->GetNode()] = nullptr;
332 delete path;
333 }
334 path = parent;
335 }
336 }
337 delete source;
338 paths.clear();
339}
340
351uint MultiCommodityFlow::PushFlow(Node &node, NodeID to, Path *path, uint accuracy,
352 uint max_saturation)
353{
354 assert(node.UnsatisfiedDemandTo(to) > 0);
355 uint flow = Clamp(node.DemandTo(to) / accuracy, 1, node.UnsatisfiedDemandTo(to));
356 flow = path->AddFlow(flow, this->job, max_saturation);
357 node.SatisfyDemandTo(to, flow);
358 return flow;
359}
360
367uint MCF1stPass::FindCycleFlow(const PathVector &path, const Path *cycle_begin)
368{
369 uint flow = UINT_MAX;
370 const Path *cycle_end = cycle_begin;
371 do {
372 flow = std::min(flow, cycle_begin->GetFlow());
373 cycle_begin = path[cycle_begin->GetNode()];
374 } while (cycle_begin != cycle_end);
375 return flow;
376}
377
384void MCF1stPass::EliminateCycle(PathVector &path, Path *cycle_begin, uint flow)
385{
386 Path *cycle_end = cycle_begin;
387 do {
388 NodeID prev = cycle_begin->GetNode();
389 cycle_begin->ReduceFlow(flow);
390 if (cycle_begin->GetFlow() == 0) {
391 PathList &node_paths = this->job[cycle_begin->GetParent()->GetNode()].paths;
392 for (PathList::iterator i = node_paths.begin(); i != node_paths.end(); ++i) {
393 if (*i == cycle_begin) {
394 node_paths.erase(i);
395 node_paths.push_back(cycle_begin);
396 break;
397 }
398 }
399 }
400 cycle_begin = path[prev];
401 Edge &edge = this->job[prev][cycle_begin->GetNode()];
402 edge.RemoveFlow(flow);
403 } while (cycle_begin != cycle_end);
404}
405
415bool MCF1stPass::EliminateCycles(PathVector &path, NodeID origin_id, NodeID next_id)
416{
417 Path *at_next_pos = path[next_id];
418
419 /* this node has already been searched */
420 if (at_next_pos == Path::invalid_path) return false;
421
422 if (at_next_pos == nullptr) {
423 /* Summarize paths; add up the paths with the same source and next hop
424 * in one path each. */
425 PathList &paths = this->job[next_id].paths;
426 PathViaMap next_hops;
427 for (PathList::iterator i = paths.begin(); i != paths.end();) {
428 Path *new_child = *i;
429 uint new_flow = new_child->GetFlow();
430 if (new_flow == 0) break;
431 if (new_child->GetOrigin() == origin_id) {
432 PathViaMap::iterator via_it = next_hops.find(new_child->GetNode());
433 if (via_it == next_hops.end()) {
434 next_hops[new_child->GetNode()] = new_child;
435 ++i;
436 } else {
437 Path *child = via_it->second;
438 child->AddFlow(new_flow);
439 new_child->ReduceFlow(new_flow);
440
441 /* We might hit end() with with the ++ here and skip the
442 * newly push_back'ed path. That's good as the flow of that
443 * path is 0 anyway. */
444 paths.erase(i++);
445 paths.push_back(new_child);
446 }
447 } else {
448 ++i;
449 }
450 }
451 bool found = false;
452 /* Search the next hops for nodes we have already visited */
453 for (PathViaMap::iterator via_it = next_hops.begin();
454 via_it != next_hops.end(); ++via_it) {
455 Path *child = via_it->second;
456 if (child->GetFlow() > 0) {
457 /* Push one child into the path vector and search this child's
458 * children. */
459 path[next_id] = child;
460 found = this->EliminateCycles(path, origin_id, child->GetNode()) || found;
461 }
462 }
463 /* All paths departing from this node have been searched. Mark as
464 * resolved if no cycles found. If cycles were found further cycles
465 * could be found in this branch, thus it has to be searched again next
466 * time we spot it.
467 */
468 path[next_id] = found ? nullptr : Path::invalid_path;
469 return found;
470 }
471
472 /* This node has already been visited => we have a cycle.
473 * Backtrack to find the exact flow. */
474 uint flow = this->FindCycleFlow(path, at_next_pos);
475 if (flow > 0) {
476 this->EliminateCycle(path, at_next_pos, flow);
477 return true;
478 }
479
480 return false;
481}
482
489{
490 bool cycles_found = false;
491 uint16_t size = this->job.Size();
492 PathVector path(size, nullptr);
493 for (NodeID node = 0; node < size; ++node) {
494 /* Starting at each node in the graph find all cycles involving this
495 * node. */
496 std::fill(path.begin(), path.end(), (Path *)nullptr);
497 cycles_found |= this->EliminateCycles(path, node, node);
498 }
499 return cycles_found;
500}
501
507{
508 PathVector paths;
509 uint16_t size = job.Size();
510 uint accuracy = job.Settings().accuracy;
511 bool more_loops;
512 std::vector<bool> finished_sources(size);
513
514 do {
515 more_loops = false;
516 for (NodeID source = 0; source < size; ++source) {
517 if (finished_sources[source]) continue;
518
519 /* First saturate the shortest paths. */
520 this->Dijkstra<DistanceAnnotation, GraphEdgeIterator>(source, paths);
521
522 Node &src_node = job[source];
523 bool source_demand_left = false;
524 for (NodeID dest = 0; dest < size; ++dest) {
525 if (src_node.UnsatisfiedDemandTo(dest) > 0) {
526 Path *path = paths[dest];
527 assert(path != nullptr);
528 /* Generally only allow paths that don't exceed the
529 * available capacity. But if no demand has been assigned
530 * yet, make an exception and allow any valid path *once*. */
531 if (path->GetFreeCapacity() > 0 && this->PushFlow(src_node, dest, path,
532 accuracy, this->max_saturation) > 0) {
533 /* If a path has been found there is a chance we can
534 * find more. */
535 more_loops = more_loops || (src_node.UnsatisfiedDemandTo(dest) > 0);
536 } else if (src_node.UnsatisfiedDemandTo(dest) == src_node.DemandTo(dest) &&
537 path->GetFreeCapacity() > INT_MIN) {
538 this->PushFlow(src_node, dest, path, accuracy, UINT_MAX);
539 }
540 if (src_node.UnsatisfiedDemandTo(dest) > 0) source_demand_left = true;
541 }
542 }
543 finished_sources[source] = !source_demand_left;
544 this->CleanupPaths(source, paths);
545 }
546 } while ((more_loops || this->EliminateCycles()) && !job.IsJobAborted());
547}
548
555{
556 this->max_saturation = UINT_MAX; // disable artificial cap on saturation
557 PathVector paths;
558 uint16_t size = job.Size();
559 uint accuracy = job.Settings().accuracy;
560 bool demand_left = true;
561 std::vector<bool> finished_sources(size);
562 while (demand_left && !job.IsJobAborted()) {
563 demand_left = false;
564 for (NodeID source = 0; source < size; ++source) {
565 if (finished_sources[source]) continue;
566
567 this->Dijkstra<CapacityAnnotation, FlowEdgeIterator>(source, paths);
568
569 Node &src_node = job[source];
570 bool source_demand_left = false;
571 for (NodeID dest = 0; dest < size; ++dest) {
572 Path *path = paths[dest];
573 if (src_node.UnsatisfiedDemandTo(dest) > 0 && path->GetFreeCapacity() > INT_MIN) {
574 this->PushFlow(src_node, dest, path, accuracy, UINT_MAX);
575 if (src_node.UnsatisfiedDemandTo(dest) > 0) {
576 demand_left = true;
577 source_demand_left = true;
578 }
579 }
580 }
581 finished_sources[source] = !source_demand_left;
582 this->CleanupPaths(source, paths);
583 }
584 }
585}
586
598template <typename T>
599bool Greater(T x_anno, T y_anno, NodeID x, NodeID y)
600{
601 if (x_anno > y_anno) return true;
602 if (x_anno < y_anno) return false;
603 return x > y;
604}
605
613 const CapacityAnnotation *y) const
614{
615 return x != y && Greater<int>(x->GetAnnotation(), y->GetAnnotation(),
616 x->GetNode(), y->GetNode());
617}
618
626 const DistanceAnnotation *y) const
627{
628 return x != y && !Greater<uint>(x->GetAnnotation(), y->GetAnnotation(),
629 x->GetNode(), y->GetNode());
630}
@ Mail
Mail.
@ Passengers
Passengers.
@ Express
Express cargo (Goods, Food, Candy, but also possible for passengers)
bool IsCargoInClass(CargoType cargo, CargoClasses cc)
Does cargo c have cargo class cc?
Definition cargotype.h:236
Capacity-based annotation for use in the Dijkstra algorithm.
Definition mcf.cpp:61
void UpdateAnnotation()
Update the cached annotation value.
Definition mcf.cpp:84
int GetAnnotation() const
Return the actual value of the annotation, in this case the capacity.
Definition mcf.cpp:79
bool IsBetter(const CapacityAnnotation *base, uint cap, int free_cap, uint dist) const
Determines if an extension to the given Path with the given parameters is better than this path.
Definition mcf.cpp:240
CapacityAnnotation(NodeID n, bool source=false)
Constructor.
Definition mcf.cpp:71
Distance-based annotation for use in the Dijkstra algorithm.
Definition mcf.cpp:24
bool IsBetter(const DistanceAnnotation *base, uint cap, int free_cap, uint dist) const
Determines if an extension to the given Path with the given parameters is better than this path.
Definition mcf.cpp:206
DistanceAnnotation(NodeID n, bool source=false)
Constructor.
Definition mcf.cpp:32
uint GetAnnotation() const
Return the actual value of the annotation, in this case the distance.
Definition mcf.cpp:40
void UpdateAnnotation()
Update the cached annotation value.
Definition mcf.cpp:45
Iterator class for getting edges from a FlowStatMap.
Definition mcf.cpp:139
FlowStat::SharesMap::const_iterator end
End of the shares map.
Definition mcf.cpp:150
FlowStat::SharesMap::const_iterator it
Current iterator in the shares map.
Definition mcf.cpp:147
void SetNode(NodeID source, NodeID node)
Setup the node to retrieve edges from.
Definition mcf.cpp:173
FlowEdgeIterator(LinkGraphJob &job)
Constructor.
Definition mcf.cpp:157
TypedIndexContainer< std::vector< NodeID >, StationID > station_to_node
Lookup table for getting NodeIDs from StationIDs.
Definition mcf.cpp:144
LinkGraphJob & job
Link graph job we're working with.
Definition mcf.cpp:141
NodeID Next()
Get the next node for which a flow exists.
Definition mcf.cpp:190
Flow descriptions by origin stations.
static const SharesMap empty_sharesmap
Static instance of FlowStat::SharesMap.
Iterator class for getting the edges in the order of their next_edge members.
Definition mcf.cpp:101
LinkGraphJob & job
Job being executed.
Definition mcf.cpp:103
std::vector< LinkGraphJob::EdgeAnnotation >::const_iterator end
Iterator pointing beyond last edge.
Definition mcf.cpp:106
std::vector< LinkGraphJob::EdgeAnnotation >::const_iterator i
Iterator pointing to current edge.
Definition mcf.cpp:105
GraphEdgeIterator(LinkGraphJob &job)
Construct a GraphEdgeIterator.
Definition mcf.cpp:114
NodeID Next()
Retrieve the ID of the node the next edge points to.
Definition mcf.cpp:130
void SetNode(NodeID, NodeID node)
Setup the node to start iterating at.
Definition mcf.cpp:120
Class for calculation jobs to be run on link graphs.
bool IsJobAborted() const
Check if job has been aborted.
const LinkGraphSettings & Settings() const
Get the link graph settings for this component.
CargoType Cargo() const
Get the cargo of the underlying link graph.
NodeID Size() const
Get the size of the underlying link graph.
MCF1stPass(LinkGraphJob &job)
Run the first pass of the MCF calculation.
Definition mcf.cpp:506
bool EliminateCycles()
Eliminate all cycles in the graph.
Definition mcf.cpp:488
uint FindCycleFlow(const PathVector &path, const Path *cycle_begin)
Find the flow along a cycle including cycle_begin in path.
Definition mcf.cpp:367
void EliminateCycle(PathVector &path, Path *cycle_begin, uint flow)
Eliminate a cycle of the given flow in the given set of paths.
Definition mcf.cpp:384
MCF2ndPass(LinkGraphJob &job)
Run the second pass of the MCF calculation which assigns all remaining demands to existing paths.
Definition mcf.cpp:554
Multi-commodity flow calculating base class.
Definition mcf.h:20
void Dijkstra(NodeID from, PathVector &paths)
A slightly modified Dijkstra algorithm.
Definition mcf.cpp:264
LinkGraphJob & job
Job we're working with.
Definition mcf.h:37
uint max_saturation
Maximum saturation for edges.
Definition mcf.h:38
uint PushFlow(Node &node, NodeID to, Path *path, uint accuracy, uint max_saturation)
Push flow along a path and update the unsatisfied_demand of the associated edge.
Definition mcf.cpp:351
void CleanupPaths(NodeID source, PathVector &paths)
Clean up paths that lead nowhere and the root path.
Definition mcf.cpp:319
A leg of a path in the link graph.
NodeID GetOrigin() const
Get the overall origin of the path.
void AddFlow(uint f)
Increase the flow on this leg only by the specified amount.
static Path * invalid_path
Static instance of an invalid path.
void Detach()
Detach this path from its parent.
int GetFreeCapacity() const
Get the free capacity of the path.
uint GetFlow() const
Get the flow on this leg.
uint distance
Sum(distance of all legs up to this one).
NodeID GetNode() const
Get the node this leg passes.
int GetCapacityRatio() const
Get capacity ratio of this path.
uint GetNumChildren() const
Get the number of "forked off" child legs of this one.
void ReduceFlow(uint f)
Reduce the flow on this leg only by the specified amount.
Path * GetParent()
Get the parent leg of this one.
uint capacity
This capacity is min(capacity) fom all edges.
int free_capacity
This capacity is min(edge.capacity - edge.flow) for the current run of Dijkstra.
static constexpr TimerGameTick::Ticks DAY_TICKS
1 day is 74 ticks; TimerGameCalendar::date_fract used to be uint16_t and incremented by 885.
A sort-of mixin that implements 'at(pos)' and 'operator[](pos)' only for a specific type.
uint DistanceMaxPlusManhattan(TileIndex t0, TileIndex t1)
Gets the biggest distance component (x or y) between the two given tiles plus the Manhattan distance,...
Definition map.cpp:206
constexpr T Clamp(const T a, const T min, const T max)
Clamp a value between an interval.
Definition math_func.hpp:79
bool Greater(T x_anno, T y_anno, NodeID x, NodeID y)
Relation that creates a weak order without duplicates.
Definition mcf.cpp:599
Declaration of Multi-Commodity-Flow solver.
Comparator for std containers.
Definition mcf.cpp:92
bool operator()(const CapacityAnnotation *x, const CapacityAnnotation *y) const
Compare two capacity annotations.
Definition mcf.cpp:612
Comparator for std containers.
Definition mcf.cpp:50
bool operator()(const DistanceAnnotation *x, const DistanceAnnotation *y) const
Compare two distance annotations.
Definition mcf.cpp:625
uint8_t accuracy
accuracy when calculating things on the link graph. low accuracy => low running time
An edge in the link graph.
Definition linkgraph.h:42
uint32_t TravelTime() const
Get edge's average travel time.
Definition linkgraph.h:56
uint capacity
Capacity of the link.
Definition linkgraph.h:43
Node of the link graph.
Definition linkgraph.h:90