OpenTTD Source 20260206-master-g4d4e37dbf1
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
352uint MultiCommodityFlow::PushFlow(Node &node, NodeID to, Path *path, uint accuracy,
353 uint max_saturation)
354{
355 assert(node.UnsatisfiedDemandTo(to) > 0);
356 uint flow = Clamp(node.DemandTo(to) / accuracy, 1, node.UnsatisfiedDemandTo(to));
357 flow = path->AddFlow(flow, this->job, max_saturation);
358 node.SatisfyDemandTo(to, flow);
359 return flow;
360}
361
368uint MCF1stPass::FindCycleFlow(const PathVector &path, const Path *cycle_begin)
369{
370 uint flow = UINT_MAX;
371 const Path *cycle_end = cycle_begin;
372 do {
373 flow = std::min(flow, cycle_begin->GetFlow());
374 cycle_begin = path[cycle_begin->GetNode()];
375 } while (cycle_begin != cycle_end);
376 return flow;
377}
378
385void MCF1stPass::EliminateCycle(PathVector &path, Path *cycle_begin, uint flow)
386{
387 Path *cycle_end = cycle_begin;
388 do {
389 NodeID prev = cycle_begin->GetNode();
390 cycle_begin->ReduceFlow(flow);
391 if (cycle_begin->GetFlow() == 0) {
392 PathList &node_paths = this->job[cycle_begin->GetParent()->GetNode()].paths;
393 for (PathList::iterator i = node_paths.begin(); i != node_paths.end(); ++i) {
394 if (*i == cycle_begin) {
395 node_paths.erase(i);
396 node_paths.push_back(cycle_begin);
397 break;
398 }
399 }
400 }
401 cycle_begin = path[prev];
402 Edge &edge = this->job[prev][cycle_begin->GetNode()];
403 edge.RemoveFlow(flow);
404 } while (cycle_begin != cycle_end);
405}
406
416bool MCF1stPass::EliminateCycles(PathVector &path, NodeID origin_id, NodeID next_id)
417{
418 Path *at_next_pos = path[next_id];
419
420 /* this node has already been searched */
421 if (at_next_pos == Path::invalid_path) return false;
422
423 if (at_next_pos == nullptr) {
424 /* Summarize paths; add up the paths with the same source and next hop
425 * in one path each. */
426 PathList &paths = this->job[next_id].paths;
427 PathViaMap next_hops;
428 for (PathList::iterator i = paths.begin(); i != paths.end();) {
429 Path *new_child = *i;
430 uint new_flow = new_child->GetFlow();
431 if (new_flow == 0) break;
432 if (new_child->GetOrigin() == origin_id) {
433 PathViaMap::iterator via_it = next_hops.find(new_child->GetNode());
434 if (via_it == next_hops.end()) {
435 next_hops[new_child->GetNode()] = new_child;
436 ++i;
437 } else {
438 Path *child = via_it->second;
439 child->AddFlow(new_flow);
440 new_child->ReduceFlow(new_flow);
441
442 /* We might hit end() with with the ++ here and skip the
443 * newly push_back'ed path. That's good as the flow of that
444 * path is 0 anyway. */
445 paths.erase(i++);
446 paths.push_back(new_child);
447 }
448 } else {
449 ++i;
450 }
451 }
452 bool found = false;
453 /* Search the next hops for nodes we have already visited */
454 for (PathViaMap::iterator via_it = next_hops.begin();
455 via_it != next_hops.end(); ++via_it) {
456 Path *child = via_it->second;
457 if (child->GetFlow() > 0) {
458 /* Push one child into the path vector and search this child's
459 * children. */
460 path[next_id] = child;
461 found = this->EliminateCycles(path, origin_id, child->GetNode()) || found;
462 }
463 }
464 /* All paths departing from this node have been searched. Mark as
465 * resolved if no cycles found. If cycles were found further cycles
466 * could be found in this branch, thus it has to be searched again next
467 * time we spot it.
468 */
469 path[next_id] = found ? nullptr : Path::invalid_path;
470 return found;
471 }
472
473 /* This node has already been visited => we have a cycle.
474 * Backtrack to find the exact flow. */
475 uint flow = this->FindCycleFlow(path, at_next_pos);
476 if (flow > 0) {
477 this->EliminateCycle(path, at_next_pos, flow);
478 return true;
479 }
480
481 return false;
482}
483
490{
491 bool cycles_found = false;
492 uint16_t size = this->job.Size();
493 PathVector path(size, nullptr);
494 for (NodeID node = 0; node < size; ++node) {
495 /* Starting at each node in the graph find all cycles involving this
496 * node. */
497 std::fill(path.begin(), path.end(), (Path *)nullptr);
498 cycles_found |= this->EliminateCycles(path, node, node);
499 }
500 return cycles_found;
501}
502
508{
509 PathVector paths;
510 uint16_t size = job.Size();
511 uint accuracy = job.Settings().accuracy;
512 bool more_loops;
513 std::vector<bool> finished_sources(size);
514
515 do {
516 more_loops = false;
517 for (NodeID source = 0; source < size; ++source) {
518 if (finished_sources[source]) continue;
519
520 /* First saturate the shortest paths. */
522
523 Node &src_node = job[source];
524 bool source_demand_left = false;
525 for (NodeID dest = 0; dest < size; ++dest) {
526 if (src_node.UnsatisfiedDemandTo(dest) > 0) {
527 Path *path = paths[dest];
528 assert(path != nullptr);
529 /* Generally only allow paths that don't exceed the
530 * available capacity. But if no demand has been assigned
531 * yet, make an exception and allow any valid path *once*. */
532 if (path->GetFreeCapacity() > 0 && this->PushFlow(src_node, dest, path,
533 accuracy, this->max_saturation) > 0) {
534 /* If a path has been found there is a chance we can
535 * find more. */
536 more_loops = more_loops || (src_node.UnsatisfiedDemandTo(dest) > 0);
537 } else if (src_node.UnsatisfiedDemandTo(dest) == src_node.DemandTo(dest) &&
538 path->GetFreeCapacity() > INT_MIN) {
539 this->PushFlow(src_node, dest, path, accuracy, UINT_MAX);
540 }
541 if (src_node.UnsatisfiedDemandTo(dest) > 0) source_demand_left = true;
542 }
543 }
544 finished_sources[source] = !source_demand_left;
545 this->CleanupPaths(source, paths);
546 }
547 } while ((more_loops || this->EliminateCycles()) && !job.IsJobAborted());
548}
549
556{
557 this->max_saturation = UINT_MAX; // disable artificial cap on saturation
558 PathVector paths;
559 uint16_t size = job.Size();
560 uint accuracy = job.Settings().accuracy;
561 bool demand_left = true;
562 std::vector<bool> finished_sources(size);
563 while (demand_left && !job.IsJobAborted()) {
564 demand_left = false;
565 for (NodeID source = 0; source < size; ++source) {
566 if (finished_sources[source]) continue;
567
569
570 Node &src_node = job[source];
571 bool source_demand_left = false;
572 for (NodeID dest = 0; dest < size; ++dest) {
573 Path *path = paths[dest];
574 if (src_node.UnsatisfiedDemandTo(dest) > 0 && path->GetFreeCapacity() > INT_MIN) {
575 this->PushFlow(src_node, dest, path, accuracy, UINT_MAX);
576 if (src_node.UnsatisfiedDemandTo(dest) > 0) {
577 demand_left = true;
578 source_demand_left = true;
579 }
580 }
581 }
582 finished_sources[source] = !source_demand_left;
583 this->CleanupPaths(source, paths);
584 }
585 }
586}
587
599template <typename T>
600bool Greater(T x_anno, T y_anno, NodeID x, NodeID y)
601{
602 if (x_anno > y_anno) return true;
603 if (x_anno < y_anno) return false;
604 return x > y;
605}
606
614 const CapacityAnnotation *y) const
615{
616 return x != y && Greater<int>(x->GetAnnotation(), y->GetAnnotation(),
617 x->GetNode(), y->GetNode());
618}
619
627 const DistanceAnnotation *y) const
628{
629 return x != y && !Greater<uint>(x->GetAnnotation(), y->GetAnnotation(),
630 x->GetNode(), y->GetNode());
631}
@ 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: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: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:507
bool EliminateCycles()
Eliminate all cycles in the graph.
Definition mcf.cpp:489
uint FindCycleFlow(const PathVector &path, const Path *cycle_begin)
Find the flow along a cycle including cycle_begin in path.
Definition mcf.cpp:368
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:385
MCF2ndPass(LinkGraphJob &job)
Run the second pass of the MCF calculation which assigns all remaining demands to existing paths.
Definition mcf.cpp:555
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:352
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:600
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:613
Comparator for std containers.
Definition mcf.cpp:50
bool operator()(const DistanceAnnotation *x, const DistanceAnnotation *y) const
Compare two distance annotations.
Definition mcf.cpp:626
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.