OpenTTD Source 20260218-master-g2123fca5ea
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
9
10#include "../stdafx.h"
11#include "../core/math_func.hpp"
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, 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
242 int free_cap, uint dist) const
243{
244 int min_cap = Path::GetCapacityRatio(std::min(base->free_capacity, free_cap), std::min(base->capacity, cap));
245 int this_cap = this->GetCapacityRatio();
246 if (min_cap == this_cap) {
247 /* If the capacities are the same and the other path isn't disconnected
248 * choose the shorter path. */
249 return base->distance == UINT_MAX ? false : (base->distance + dist < this->distance);
250 } else {
251 return min_cap > this_cap;
252 }
253}
254
264template <class Tannotation, class Tedge_iterator>
265void MultiCommodityFlow::Dijkstra(NodeID source_node, PathVector &paths)
266{
267 typedef std::set<Tannotation *, typename Tannotation::Comparator> AnnoSet;
268 Tedge_iterator iter(this->job);
269 uint16_t size = this->job.Size();
270 AnnoSet annos;
271 paths.resize(size, nullptr);
272 for (NodeID node = 0; node < size; ++node) {
273 Tannotation *anno = new Tannotation(node, node == source_node);
274 anno->UpdateAnnotation();
275 annos.insert(anno);
276 paths[node] = anno;
277 }
278 while (!annos.empty()) {
279 typename AnnoSet::iterator i = annos.begin();
280 Tannotation *source = *i;
281 annos.erase(i);
282 NodeID from = source->GetNode();
283 iter.SetNode(source_node, from);
284 for (NodeID to = iter.Next(); to != INVALID_NODE; to = iter.Next()) {
285 if (to == from) continue; // Not a real edge but a consumption sign.
286 const Edge &edge = this->job[from][to];
287 uint capacity = edge.base.capacity;
288 if (this->max_saturation != UINT_MAX) {
289 capacity *= this->max_saturation;
290 capacity /= 100;
291 if (capacity == 0) capacity = 1;
292 }
293 /* Prioritize the fastest route for passengers, mail and express cargo,
294 * and the shortest route for other classes of cargo.
295 * In-between stops are punished with a 1 tile or 1 day penalty. */
296 bool express = IsCargoInClass(this->job.Cargo(), CargoClass::Passengers) ||
297 IsCargoInClass(this->job.Cargo(), CargoClass::Mail) ||
298 IsCargoInClass(this->job.Cargo(), CargoClass::Express);
299 uint distance = DistanceMaxPlusManhattan(this->job[from].base.xy, this->job[to].base.xy) + 1;
300 /* Compute a default travel time from the distance and an average speed of 1 tile/day. */
301 uint time = (edge.base.TravelTime() != 0) ? edge.base.TravelTime() + Ticks::DAY_TICKS : distance * Ticks::DAY_TICKS;
302 uint distance_anno = express ? time : distance;
303
304 Tannotation *dest = static_cast<Tannotation *>(paths[to]);
305 if (dest->IsBetter(source, capacity, capacity - edge.Flow(), distance_anno)) {
306 annos.erase(dest);
307 dest->Fork(source, capacity, capacity - edge.Flow(), distance_anno);
308 dest->UpdateAnnotation();
309 annos.insert(dest);
310 }
311 }
312 }
313}
314
320void MultiCommodityFlow::CleanupPaths(NodeID source_id, PathVector &paths)
321{
322 Path *source = paths[source_id];
323 paths[source_id] = nullptr;
324 for (PathVector::iterator i = paths.begin(); i != paths.end(); ++i) {
325 Path *path = *i;
326 if (path == nullptr) continue;
327 if (path->GetParent() == source) path->Detach();
328 while (path != source && path != nullptr && path->GetFlow() == 0) {
329 Path *parent = path->GetParent();
330 path->Detach();
331 if (path->GetNumChildren() == 0) {
332 paths[path->GetNode()] = nullptr;
333 delete path;
334 }
335 path = parent;
336 }
337 }
338 delete source;
339 paths.clear();
340}
341
353uint MultiCommodityFlow::PushFlow(Node &node, NodeID to, Path *path, uint accuracy,
354 uint max_saturation)
355{
356 assert(node.UnsatisfiedDemandTo(to) > 0);
357 uint flow = Clamp(node.DemandTo(to) / accuracy, 1, node.UnsatisfiedDemandTo(to));
358 flow = path->AddFlow(flow, this->job, max_saturation);
359 node.SatisfyDemandTo(to, flow);
360 return flow;
361}
362
369uint MCF1stPass::FindCycleFlow(const PathVector &path, const Path *cycle_begin)
370{
371 uint flow = UINT_MAX;
372 const Path *cycle_end = cycle_begin;
373 do {
374 flow = std::min(flow, cycle_begin->GetFlow());
375 cycle_begin = path[cycle_begin->GetNode()];
376 } while (cycle_begin != cycle_end);
377 return flow;
378}
379
386void MCF1stPass::EliminateCycle(PathVector &path, Path *cycle_begin, uint flow)
387{
388 Path *cycle_end = cycle_begin;
389 do {
390 NodeID prev = cycle_begin->GetNode();
391 cycle_begin->ReduceFlow(flow);
392 if (cycle_begin->GetFlow() == 0) {
393 PathList &node_paths = this->job[cycle_begin->GetParent()->GetNode()].paths;
394 for (PathList::iterator i = node_paths.begin(); i != node_paths.end(); ++i) {
395 if (*i == cycle_begin) {
396 node_paths.erase(i);
397 node_paths.push_back(cycle_begin);
398 break;
399 }
400 }
401 }
402 cycle_begin = path[prev];
403 Edge &edge = this->job[prev][cycle_begin->GetNode()];
404 edge.RemoveFlow(flow);
405 } while (cycle_begin != cycle_end);
406}
407
417bool MCF1stPass::EliminateCycles(PathVector &path, NodeID origin_id, NodeID next_id)
418{
419 Path *at_next_pos = path[next_id];
420
421 /* this node has already been searched */
422 if (at_next_pos == Path::invalid_path) return false;
423
424 if (at_next_pos == nullptr) {
425 /* Summarize paths; add up the paths with the same source and next hop
426 * in one path each. */
427 PathList &paths = this->job[next_id].paths;
428 PathViaMap next_hops;
429 for (PathList::iterator i = paths.begin(); i != paths.end();) {
430 Path *new_child = *i;
431 uint new_flow = new_child->GetFlow();
432 if (new_flow == 0) break;
433 if (new_child->GetOrigin() == origin_id) {
434 PathViaMap::iterator via_it = next_hops.find(new_child->GetNode());
435 if (via_it == next_hops.end()) {
436 next_hops[new_child->GetNode()] = new_child;
437 ++i;
438 } else {
439 Path *child = via_it->second;
440 child->AddFlow(new_flow);
441 new_child->ReduceFlow(new_flow);
442
443 /* We might hit end() with with the ++ here and skip the
444 * newly push_back'ed path. That's good as the flow of that
445 * path is 0 anyway. */
446 paths.erase(i++);
447 paths.push_back(new_child);
448 }
449 } else {
450 ++i;
451 }
452 }
453 bool found = false;
454 /* Search the next hops for nodes we have already visited */
455 for (PathViaMap::iterator via_it = next_hops.begin();
456 via_it != next_hops.end(); ++via_it) {
457 Path *child = via_it->second;
458 if (child->GetFlow() > 0) {
459 /* Push one child into the path vector and search this child's
460 * children. */
461 path[next_id] = child;
462 found = this->EliminateCycles(path, origin_id, child->GetNode()) || found;
463 }
464 }
465 /* All paths departing from this node have been searched. Mark as
466 * resolved if no cycles found. If cycles were found further cycles
467 * could be found in this branch, thus it has to be searched again next
468 * time we spot it.
469 */
470 path[next_id] = found ? nullptr : Path::invalid_path;
471 return found;
472 }
473
474 /* This node has already been visited => we have a cycle.
475 * Backtrack to find the exact flow. */
476 uint flow = this->FindCycleFlow(path, at_next_pos);
477 if (flow > 0) {
478 this->EliminateCycle(path, at_next_pos, flow);
479 return true;
480 }
481
482 return false;
483}
484
491{
492 bool cycles_found = false;
493 uint16_t size = this->job.Size();
494 PathVector path(size, nullptr);
495 for (NodeID node = 0; node < size; ++node) {
496 /* Starting at each node in the graph find all cycles involving this
497 * node. */
498 std::fill(path.begin(), path.end(), (Path *)nullptr);
499 cycles_found |= this->EliminateCycles(path, node, node);
500 }
501 return cycles_found;
502}
503
509{
510 PathVector paths;
511 uint16_t size = job.Size();
512 uint accuracy = job.Settings().accuracy;
513 bool more_loops;
514 std::vector<bool> finished_sources(size);
515
516 do {
517 more_loops = false;
518 for (NodeID source = 0; source < size; ++source) {
519 if (finished_sources[source]) continue;
520
521 /* First saturate the shortest paths. */
523
524 Node &src_node = job[source];
525 bool source_demand_left = false;
526 for (NodeID dest = 0; dest < size; ++dest) {
527 if (src_node.UnsatisfiedDemandTo(dest) > 0) {
528 Path *path = paths[dest];
529 assert(path != nullptr);
530 /* Generally only allow paths that don't exceed the
531 * available capacity. But if no demand has been assigned
532 * yet, make an exception and allow any valid path *once*. */
533 if (path->GetFreeCapacity() > 0 && this->PushFlow(src_node, dest, path,
534 accuracy, this->max_saturation) > 0) {
535 /* If a path has been found there is a chance we can
536 * find more. */
537 more_loops = more_loops || (src_node.UnsatisfiedDemandTo(dest) > 0);
538 } else if (src_node.UnsatisfiedDemandTo(dest) == src_node.DemandTo(dest) &&
539 path->GetFreeCapacity() > INT_MIN) {
540 this->PushFlow(src_node, dest, path, accuracy, UINT_MAX);
541 }
542 if (src_node.UnsatisfiedDemandTo(dest) > 0) source_demand_left = true;
543 }
544 }
545 finished_sources[source] = !source_demand_left;
546 this->CleanupPaths(source, paths);
547 }
548 } while ((more_loops || this->EliminateCycles()) && !job.IsJobAborted());
549}
550
557{
558 this->max_saturation = UINT_MAX; // disable artificial cap on saturation
559 PathVector paths;
560 uint16_t size = job.Size();
561 uint accuracy = job.Settings().accuracy;
562 bool demand_left = true;
563 std::vector<bool> finished_sources(size);
564 while (demand_left && !job.IsJobAborted()) {
565 demand_left = false;
566 for (NodeID source = 0; source < size; ++source) {
567 if (finished_sources[source]) continue;
568
570
571 Node &src_node = job[source];
572 bool source_demand_left = false;
573 for (NodeID dest = 0; dest < size; ++dest) {
574 Path *path = paths[dest];
575 if (src_node.UnsatisfiedDemandTo(dest) > 0 && path->GetFreeCapacity() > INT_MIN) {
576 this->PushFlow(src_node, dest, path, accuracy, UINT_MAX);
577 if (src_node.UnsatisfiedDemandTo(dest) > 0) {
578 demand_left = true;
579 source_demand_left = true;
580 }
581 }
582 }
583 finished_sources[source] = !source_demand_left;
584 this->CleanupPaths(source, paths);
585 }
586 }
587}
588
601template <typename T>
602bool Greater(T x_anno, T y_anno, NodeID x, NodeID y)
603{
604 if (x_anno > y_anno) return true;
605 if (x_anno < y_anno) return false;
606 return x > y;
607}
608
616 const CapacityAnnotation *y) const
617{
618 return x != y && Greater<int>(x->GetAnnotation(), y->GetAnnotation(),
619 x->GetNode(), y->GetNode());
620}
621
629 const DistanceAnnotation *y) const
630{
631 return x != y && !Greater<uint>(x->GetAnnotation(), y->GetAnnotation(),
632 x->GetNode(), y->GetNode());
633}
@ Mail
Mail.
Definition cargotype.h:51
@ Passengers
Passengers.
Definition cargotype.h:50
@ Express
Express cargo (Goods, Food, Candy, but also possible for passengers).
Definition cargotype.h:52
bool IsCargoInClass(CargoType cargo, CargoClasses cc)
Does cargo c have cargo class cc?
Definition cargotype.h:237
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:241
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, 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
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.
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.
MCF1stPass(LinkGraphJob &job)
Run the first pass of the MCF calculation.
Definition mcf.cpp:508
bool EliminateCycles()
Eliminate all cycles in the graph.
Definition mcf.cpp:490
uint FindCycleFlow(const PathVector &path, const Path *cycle_begin)
Find the flow along a cycle including cycle_begin in path.
Definition mcf.cpp:369
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:386
MCF2ndPass(LinkGraphJob &job)
Run the second pass of the MCF calculation which assigns all remaining demands to existing paths.
Definition mcf.cpp:556
MultiCommodityFlow(LinkGraphJob &job)
Constructor.
Definition mcf.h:26
void Dijkstra(NodeID from, PathVector &paths)
A slightly modified Dijkstra algorithm.
Definition mcf.cpp:265
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:353
void CleanupPaths(NodeID source, PathVector &paths)
Clean up paths that lead nowhere and the root path.
Definition mcf.cpp:320
A leg of a path in the link graph.
NodeID GetOrigin() const
Get the overall origin of the path.
Path(NodeID n, bool source=false)
Create a leg of a path in the link graph.
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
Integer math functions.
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:602
Declaration of Multi-Commodity-Flow solver.
A number of safeguards to prevent using unsafe methods.
Definition of base types and functions in a cross-platform compatible way.
Comparator for std containers.
Definition mcf.cpp:92
bool operator()(const CapacityAnnotation *x, const CapacityAnnotation *y) const
Compare two capacity annotations.
Definition mcf.cpp:615
Comparator for std containers.
Definition mcf.cpp:50
bool operator()(const DistanceAnnotation *x, const DistanceAnnotation *y) const
Compare two distance annotations.
Definition mcf.cpp:628
uint32_t TravelTime() const
Get edge's average travel time.
Definition linkgraph.h:56
uint capacity
Capacity of the link.
Definition linkgraph.h:43
Definition of the tick-based game-timer.