OpenTTD: src/linkgraph/mcf.cpp Source File

OpenTTD

- Main Page
- Related Pages
- Modules
- Namespaces
- Data Structures
- Files

- File List
- Globals

mcf.cpp
Go to the documentation of this file.
1
3 #include "../stdafx.h"
4 #include "../core/math_func.hpp"
5 #include "mcf.h"
6 #include <set>
7
8 #include "../safeguards.h"
9
10 typedef std::map<NodeID, Path *> PathViaMap;
11
17 class DistanceAnnotation : public Path {
18 public:
19
25 DistanceAnnotation(NodeID n, bool source = false) : Path(n, source) {}
26
27 bool IsBetter(const DistanceAnnotation *base, uint cap, int free_cap, uint dist) const;
28
33 inline uint GetAnnotation() const { return this->distance; }
34
38 inline void UpdateAnnotation() { }
39
43 struct Comparator {
44 bool operator()(const DistanceAnnotation *x, const DistanceAnnotation *y) const;
45 };
46 };
47
54 class CapacityAnnotation : public Path {
55 int cached_annotation;
56
57 public:
58
64 CapacityAnnotation(NodeID n, bool source = false) : Path(n, source) {}
65
66 bool IsBetter(const CapacityAnnotation *base, uint cap, int free_cap, uint dist) const;
67
72 inline int GetAnnotation() const { return this->cached_annotation; }
73
77 inline void UpdateAnnotation()
78 {
79 this->cached_annotation = this->GetCapacityRatio();
80 }
81
85 struct Comparator {
86 bool operator()(const CapacityAnnotation *x, const CapacityAnnotation *y) const;
87 };
88 };
89
94 class GraphEdgeIterator {
95 private:
96 LinkGraphJob &job;
97 EdgeIterator i;
98 EdgeIterator end;
99
100 public:
101
106 GraphEdgeIterator(LinkGraphJob &job) : job(job),
107 i(NULL, NULL, INVALID_NODE), end(NULL, NULL, INVALID_NODE)
108 {}
109
115 void SetNode(NodeID source, NodeID node)
116 {
117 this->i = this->job[node].Begin();
118 this->end = this->job[node].End();
119 }
120
125 NodeID Next()
126 {
127 return this->i != this->end ? (this->i++)->first : INVALID_NODE;
128 }
129 };
130
134 class FlowEdgeIterator {
135 private:
136 LinkGraphJob &job;
137
139 std::vector<NodeID> station_to_node;
140
142 FlowStat::SharesMap::const_iterator it;
143
145 FlowStat::SharesMap::const_iterator end;
146 public:
147
152 FlowEdgeIterator(LinkGraphJob &job) : job(job)
153 {
154 for (NodeID i = 0; i < job.Size(); ++i) {
155 StationID st = job[i].Station();
156 if (st >= this->station_to_node.size()) {
157 this->station_to_node.resize(st + 1);
158 }
159 this->station_to_node[st] = i;
160 }
161 }
162
168 void SetNode(NodeID source, NodeID node)
169 {
170 const FlowStatMap &flows = this->job[node].Flows();
171 FlowStatMap::const_iterator it = flows.find(this->job[source].Station());
172 if (it != flows.end()) {
173 this->it = it->second.GetShares()->begin();
174 this->end = it->second.GetShares()->end();
175 } else {
176 this->it = FlowStat::empty_sharesmap.begin();
177 this->end = FlowStat::empty_sharesmap.end();
178 }
179 }
180
185 NodeID Next()
186 {
187 if (this->it == this->end) return INVALID_NODE;
188 return this->station_to_node[(this->it++)->second];
189 }
190 };
191
201 bool DistanceAnnotation::IsBetter(const DistanceAnnotation *base, uint cap,
202 int free_cap, uint dist) const
203 {
204 /* If any of the paths is disconnected, the other one is better. If both
205 * are disconnected, this path is better.*/
206 if (base->distance == UINT_MAX) {
207 return false;
208 } else if (this->distance == UINT_MAX) {
209 return true;
210 }
211
212 if (free_cap > 0 && base->free_capacity > 0) {
213 /* If both paths have capacity left, compare their distances.
214 * If the other path has capacity left and this one hasn't, the
215 * other one's better (thus, return true). */
216 return this->free_capacity > 0 ? (base->distance + dist < this->distance) : true;
217 } else {
218 /* If the other path doesn't have capacity left, but this one has,
219 * the other one is worse (thus, return false).
220 * If both paths are out of capacity, do the regular distance
221 * comparison. */
222 return this->free_capacity > 0 ? false : (base->distance + dist < this->distance);
223 }
224 }
225
235 bool CapacityAnnotation::IsBetter(const CapacityAnnotation *base, uint cap,
236 int free_cap, uint dist) const
237 {
238 int min_cap = Path::GetCapacityRatio(min(base->free_capacity, free_cap), min(base->capacity, cap));
239 int this_cap = this->GetCapacityRatio();
240 if (min_cap == this_cap) {
241 /* If the capacities are the same and the other path isn't disconnected
242 * choose the shorter path. */
243 return base->distance == UINT_MAX ? false : (base->distance + dist < this->distance);
244 } else {
245 return min_cap > this_cap;
246 }
247 }
248
258 template<class Tannotation, class Tedge_iterator>
259 void MultiCommodityFlow::Dijkstra(NodeID source_node, PathVector &paths)
260 {
261 typedef std::set<Tannotation *, typename Tannotation::Comparator> AnnoSet;
262 Tedge_iterator iter(this->job);
263 uint size = this->job.Size();
264 AnnoSet annos;
265 paths.resize(size, NULL);
266 for (NodeID node = 0; node < size; ++node) {
267 Tannotation *anno = new Tannotation(node, node == source_node);
268 anno->UpdateAnnotation();
269 annos.insert(anno);
270 paths[node] = anno;
271 }
272 while (!annos.empty()) {
273 typename AnnoSet::iterator i = annos.begin();
274 Tannotation *source = *i;
275 annos.erase(i);
276 NodeID from = source->GetNode();
277 iter.SetNode(source_node, from);
278 for (NodeID to = iter.Next(); to != INVALID_NODE; to = iter.Next()) {
279 if (to == from) continue; // Not a real edge but a consumption sign.
280 Edge edge = this->job[from][to];
281 uint capacity = edge.Capacity();
282 if (this->max_saturation != UINT_MAX) {
283 capacity *= this->max_saturation;
284 capacity /= 100;
285 if (capacity == 0) capacity = 1;
286 }
287 /* punish in-between stops a little */
288 uint distance = DistanceMaxPlusManhattan(this->job[from].XY(), this->job[to].XY()) + 1;
289 Tannotation *dest = static_cast<Tannotation *>(paths[to]);
290 if (dest->IsBetter(source, capacity, capacity - edge.Flow(), distance)) {
291 annos.erase(dest);
292 dest->Fork(source, capacity, capacity - edge.Flow(), distance);
293 dest->UpdateAnnotation();
294 annos.insert(dest);
295 }
296 }
297 }
298 }
299
305 void MultiCommodityFlow::CleanupPaths(NodeID source_id, PathVector &paths)
306 {
307 Path *source = paths[source_id];
308 paths[source_id] = NULL;
309 for (PathVector::iterator i = paths.begin(); i != paths.end(); ++i) {
310 Path *path = *i;
311 if (path == NULL) continue;
312 if (path->GetParent() == source) path->Detach();
313 while (path != source && path != NULL && path->GetFlow() == 0) {
314 Path *parent = path->GetParent();
315 path->Detach();
316 if (path->GetNumChildren() == 0) {
317 paths[path->GetNode()] = NULL;
318 delete path;
319 }
320 path = parent;
321 }
322 }
323 delete source;
324 paths.clear();
325 }
326
336 uint MultiCommodityFlow::PushFlow(Edge &edge, Path *path, uint accuracy,
337 uint max_saturation)
338 {
339 assert(edge.UnsatisfiedDemand() > 0);
340 uint flow = Clamp(edge.Demand() / accuracy, 1, edge.UnsatisfiedDemand());
341 flow = path->AddFlow(flow, this->job, max_saturation);
342 edge.SatisfyDemand(flow);
343 return flow;
344 }
345
352 uint MCF1stPass::FindCycleFlow(const PathVector &path, const Path *cycle_begin)
353 {
354 uint flow = UINT_MAX;
355 const Path *cycle_end = cycle_begin;
356 do {
357 flow = min(flow, cycle_begin->GetFlow());
358 cycle_begin = path[cycle_begin->GetNode()];
359 } while (cycle_begin != cycle_end);
360 return flow;
361 }
362
369 void MCF1stPass::EliminateCycle(PathVector &path, Path *cycle_begin, uint flow)
370 {
371 Path *cycle_end = cycle_begin;
372 do {
373 NodeID prev = cycle_begin->GetNode();
374 cycle_begin->ReduceFlow(flow);
375 if (cycle_begin->GetFlow() == 0) {
376 PathList &node_paths = this->job[cycle_begin->GetParent()->GetNode()].Paths();
377 for (PathList::iterator i = node_paths.begin(); i != node_paths.end(); ++i) {
378 if (*i == cycle_begin) {
379 node_paths.erase(i);
380 node_paths.push_back(cycle_begin);
381 break;
382 }
383 }
384 }
385 cycle_begin = path[prev];
386 Edge edge = this->job[prev][cycle_begin->GetNode()];
387 edge.RemoveFlow(flow);
388 } while (cycle_begin != cycle_end);
389 }
390
400 bool MCF1stPass::EliminateCycles(PathVector &path, NodeID origin_id, NodeID next_id)
401 {
402 Path *at_next_pos = path[next_id];
403
404 /* this node has already been searched */
405 if (at_next_pos == Path::invalid_path) return false;
406
407 if (at_next_pos == NULL) {
408 /* Summarize paths; add up the paths with the same source and next hop
409 * in one path each. */
410 PathList &paths = this->job[next_id].Paths();
411 PathViaMap next_hops;
412 for (PathList::iterator i = paths.begin(); i != paths.end();) {
413 Path *new_child = *i;
414 uint new_flow = new_child->GetFlow();
415 if (new_flow == 0) break;
416 if (new_child->GetOrigin() == origin_id) {
417 PathViaMap::iterator via_it = next_hops.find(new_child->GetNode());
418 if (via_it == next_hops.end()) {
419 next_hops[new_child->GetNode()] = new_child;
420 ++i;
421 } else {
422 Path *child = via_it->second;
423 child->AddFlow(new_flow);
424 new_child->ReduceFlow(new_flow);
425
426 /* We might hit end() with with the ++ here and skip the
427 * newly push_back'ed path. That's good as the flow of that
428 * path is 0 anyway. */
429 paths.erase(i++);
430 paths.push_back(new_child);
431 }
432 } else {
433 ++i;
434 }
435 }
436 bool found = false;
437 /* Search the next hops for nodes we have already visited */
438 for (PathViaMap::iterator via_it = next_hops.begin();
439 via_it != next_hops.end(); ++via_it) {
440 Path *child = via_it->second;
441 if (child->GetFlow() > 0) {
442 /* Push one child into the path vector and search this child's
443 * children. */
444 path[next_id] = child;
445 found = this->EliminateCycles(path, origin_id, child->GetNode()) || found;
446 }
447 }
448 /* All paths departing from this node have been searched. Mark as
449 * resolved if no cycles found. If cycles were found further cycles
450 * could be found in this branch, thus it has to be searched again next
451 * time we spot it.
452 */
453 path[next_id] = found ? NULL : Path::invalid_path;
454 return found;
455 }
456
457 /* This node has already been visited => we have a cycle.
458 * Backtrack to find the exact flow. */
459 uint flow = this->FindCycleFlow(path, at_next_pos);
460 if (flow > 0) {
461 this->EliminateCycle(path, at_next_pos, flow);
462 return true;
463 }
464
465 return false;
466 }
467
473 bool MCF1stPass::EliminateCycles()
474 {
475 bool cycles_found = false;
476 uint size = this->job.Size();
477 PathVector path(size, NULL);
478 for (NodeID node = 0; node < size; ++node) {
479 /* Starting at each node in the graph find all cycles involving this
480 * node. */
481 std::fill(path.begin(), path.end(), (Path *)NULL);
482 cycles_found |= this->EliminateCycles(path, node, node);
483 }
484 return cycles_found;
485 }
486
491 MCF1stPass::MCF1stPass(LinkGraphJob &job) : MultiCommodityFlow(job)
492 {
493 PathVector paths;
494 uint size = job.Size();
495 uint accuracy = job.Settings().accuracy;
496 bool more_loops;
497
498 do {
499 more_loops = false;
500 for (NodeID source = 0; source < size; ++source) {
501 /* First saturate the shortest paths. */
502 this->Dijkstra<DistanceAnnotation, GraphEdgeIterator>(source, paths);
503
504 for (NodeID dest = 0; dest < size; ++dest) {
505 Edge edge = job[source][dest];
506 if (edge.UnsatisfiedDemand() > 0) {
507 Path *path = paths[dest];
508 assert(path != NULL);
509 /* Generally only allow paths that don't exceed the
510 * available capacity. But if no demand has been assigned
511 * yet, make an exception and allow any valid path *once*. */
512 if (path->GetFreeCapacity() > 0 && this->PushFlow(edge, path,
513 accuracy, this->max_saturation) > 0) {
514 /* If a path has been found there is a chance we can
515 * find more. */
516 more_loops = more_loops || (edge.UnsatisfiedDemand() > 0);
517 } else if (edge.UnsatisfiedDemand() == edge.Demand() &&
518 path->GetFreeCapacity() > INT_MIN) {
519 this->PushFlow(edge, path, accuracy, UINT_MAX);
520 }
521 }
522 }
523 this->CleanupPaths(source, paths);
524 }
525 } while (more_loops || this->EliminateCycles());
526 }
527
533 MCF2ndPass::MCF2ndPass(LinkGraphJob &job) : MultiCommodityFlow(job)
534 {
535 this->max_saturation = UINT_MAX; // disable artificial cap on saturation
536 PathVector paths;
537 uint size = job.Size();
538 uint accuracy = job.Settings().accuracy;
539 bool demand_left = true;
540 while (demand_left) {
541 demand_left = false;
542 for (NodeID source = 0; source < size; ++source) {
543 this->Dijkstra<CapacityAnnotation, FlowEdgeIterator>(source, paths);
544 for (NodeID dest = 0; dest < size; ++dest) {
545 Edge edge = this->job[source][dest];
546 Path *path = paths[dest];
547 if (edge.UnsatisfiedDemand() > 0 && path->GetFreeCapacity() > INT_MIN) {
548 this->PushFlow(edge, path, accuracy, UINT_MAX);
549 if (edge.UnsatisfiedDemand() > 0) demand_left = true;
550 }
551 }
552 this->CleanupPaths(source, paths);
553 }
554 }
555 }
556
568 template <typename T>
569 bool Greater(T x_anno, T y_anno, NodeID x, NodeID y)
570 {
571 if (x_anno > y_anno) return true;
572 if (x_anno < y_anno) return false;
573 return x > y;
574 }
575
582 bool CapacityAnnotation::Comparator::operator()(const CapacityAnnotation *x,
583 const CapacityAnnotation *y) const
584 {
585 return x != y && Greater<int>(x->GetAnnotation(), y->GetAnnotation(),
586 x->GetNode(), y->GetNode());
587 }
588
595 bool DistanceAnnotation::Comparator::operator()(const DistanceAnnotation *x,
596 const DistanceAnnotation *y) const
597 {
598 return x != y && !Greater<uint>(x->GetAnnotation(), y->GetAnnotation(),
599 x->GetNode(), y->GetNode());
600 }