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 struct Comparator {
39 bool operator()(const DistanceAnnotation *x, const DistanceAnnotation *y) const;
40 };
41 };
42
49 class CapacityAnnotation : public Path {
50 public:
51
57 CapacityAnnotation(NodeID n, bool source = false) : Path(n, source) {}
58
59 bool IsBetter(const CapacityAnnotation *base, uint cap, int free_cap, uint dist) const;
60
65 inline int GetAnnotation() const { return this->GetCapacityRatio(); }
66
70 struct Comparator {
71 bool operator()(const CapacityAnnotation *x, const CapacityAnnotation *y) const;
72 };
73 };
74
79 class GraphEdgeIterator {
80 private:
81 LinkGraphJob &job;
82 EdgeIterator i;
83 EdgeIterator end;
84
85 public:
86
91 GraphEdgeIterator(LinkGraphJob &job) : job(job),
92 i(NULL, NULL, INVALID_NODE), end(NULL, NULL, INVALID_NODE)
93 {}
94
100 void SetNode(NodeID source, NodeID node)
101 {
102 this->i = this->job[node].Begin();
103 this->end = this->job[node].End();
104 }
105
110 NodeID Next()
111 {
112 return this->i != this->end ? (this->i++)->first : INVALID_NODE;
113 }
114 };
115
119 class FlowEdgeIterator {
120 private:
121 LinkGraphJob &job;
122
124 std::map<StationID, NodeID> station_to_node;
125
127 FlowStat::SharesMap::const_iterator it;
128
130 FlowStat::SharesMap::const_iterator end;
131 public:
132
137 FlowEdgeIterator(LinkGraphJob &job) : job(job)
138 {
139 for (NodeID i = 0; i < job.Size(); ++i) {
140 this->station_to_node[job[i].Station()] = i;
141 }
142 }
143
149 void SetNode(NodeID source, NodeID node)
150 {
151 static const FlowStat::SharesMap empty;
152 const FlowStatMap &flows = this->job[node].Flows();
153 FlowStatMap::const_iterator it = flows.find(this->job[source].Station());
154 if (it != flows.end()) {
155 this->it = it->second.GetShares()->begin();
156 this->end = it->second.GetShares()->end();
157 } else {
158 this->it = empty.begin();
159 this->end = empty.end();
160 }
161 }
162
167 NodeID Next()
168 {
169 if (this->it == this->end) return INVALID_NODE;
170 return this->station_to_node[(this->it++)->second];
171 }
172 };
173
183 bool DistanceAnnotation::IsBetter(const DistanceAnnotation *base, uint cap,
184 int free_cap, uint dist) const
185 {
186 /* If any of the paths is disconnected, the other one is better. If both
187 * are disconnected, this path is better.*/
188 if (base->distance == UINT_MAX) {
189 return false;
190 } else if (this->distance == UINT_MAX) {
191 return true;
192 }
193
194 if (free_cap > 0 && base->free_capacity > 0) {
195 /* If both paths have capacity left, compare their distances.
196 * If the other path has capacity left and this one hasn't, the
197 * other one's better (thus, return true). */
198 return this->free_capacity > 0 ? (base->distance + dist < this->distance) : true;
199 } else {
200 /* If the other path doesn't have capacity left, but this one has,
201 * the other one is worse (thus, return false).
202 * If both paths are out of capacity, do the regular distance
203 * comparison. */
204 return this->free_capacity > 0 ? false : (base->distance + dist < this->distance);
205 }
206 }
207
217 bool CapacityAnnotation::IsBetter(const CapacityAnnotation *base, uint cap,
218 int free_cap, uint dist) const
219 {
220 int min_cap = Path::GetCapacityRatio(min(base->free_capacity, free_cap), min(base->capacity, cap));
221 int this_cap = this->GetCapacityRatio();
222 if (min_cap == this_cap) {
223 /* If the capacities are the same and the other path isn't disconnected
224 * choose the shorter path. */
225 return base->distance == UINT_MAX ? false : (base->distance + dist < this->distance);
226 } else {
227 return min_cap > this_cap;
228 }
229 }
230
240 template<class Tannotation, class Tedge_iterator>
241 void MultiCommodityFlow::Dijkstra(NodeID source_node, PathVector &paths)
242 {
243 typedef std::set<Tannotation *, typename Tannotation::Comparator> AnnoSet;
244 Tedge_iterator iter(this->job);
245 uint size = this->job.Size();
246 AnnoSet annos;
247 paths.resize(size, NULL);
248 for (NodeID node = 0; node < size; ++node) {
249 Tannotation *anno = new Tannotation(node, node == source_node);
250 annos.insert(anno);
251 paths[node] = anno;
252 }
253 while (!annos.empty()) {
254 typename AnnoSet::iterator i = annos.begin();
255 Tannotation *source = *i;
256 annos.erase(i);
257 NodeID from = source->GetNode();
258 iter.SetNode(source_node, from);
259 for (NodeID to = iter.Next(); to != INVALID_NODE; to = iter.Next()) {
260 if (to == from) continue; // Not a real edge but a consumption sign.
261 Edge edge = this->job[from][to];
262 uint capacity = edge.Capacity();
263 if (this->max_saturation != UINT_MAX) {
264 capacity *= this->max_saturation;
265 capacity /= 100;
266 if (capacity == 0) capacity = 1;
267 }
268 /* punish in-between stops a little */
269 uint distance = DistanceMaxPlusManhattan(this->job[from].XY(), this->job[to].XY()) + 1;
270 Tannotation *dest = static_cast<Tannotation *>(paths[to]);
271 if (dest->IsBetter(source, capacity, capacity - edge.Flow(), distance)) {
272 annos.erase(dest);
273 dest->Fork(source, capacity, capacity - edge.Flow(), distance);
274 annos.insert(dest);
275 }
276 }
277 }
278 }
279
285 void MultiCommodityFlow::CleanupPaths(NodeID source_id, PathVector &paths)
286 {
287 Path *source = paths[source_id];
288 paths[source_id] = NULL;
289 for (PathVector::iterator i = paths.begin(); i != paths.end(); ++i) {
290 Path *path = *i;
291 if (path == NULL) continue;
292 if (path->GetParent() == source) path->Detach();
293 while (path != source && path != NULL && path->GetFlow() == 0) {
294 Path *parent = path->GetParent();
295 path->Detach();
296 if (path->GetNumChildren() == 0) {
297 paths[path->GetNode()] = NULL;
298 delete path;
299 }
300 path = parent;
301 }
302 }
303 delete source;
304 paths.clear();
305 }
306
316 uint MultiCommodityFlow::PushFlow(Edge &edge, Path *path, uint accuracy,
317 uint max_saturation)
318 {
319 assert(edge.UnsatisfiedDemand() > 0);
320 uint flow = Clamp(edge.Demand() / accuracy, 1, edge.UnsatisfiedDemand());
321 flow = path->AddFlow(flow, this->job, max_saturation);
322 edge.SatisfyDemand(flow);
323 return flow;
324 }
325
332 uint MCF1stPass::FindCycleFlow(const PathVector &path, const Path *cycle_begin)
333 {
334 uint flow = UINT_MAX;
335 const Path *cycle_end = cycle_begin;
336 do {
337 flow = min(flow, cycle_begin->GetFlow());
338 cycle_begin = path[cycle_begin->GetNode()];
339 } while (cycle_begin != cycle_end);
340 return flow;
341 }
342
349 void MCF1stPass::EliminateCycle(PathVector &path, Path *cycle_begin, uint flow)
350 {
351 Path *cycle_end = cycle_begin;
352 do {
353 NodeID prev = cycle_begin->GetNode();
354 cycle_begin->ReduceFlow(flow);
355 if (cycle_begin->GetFlow() == 0) {
356 PathList &node_paths = this->job[cycle_begin->GetParent()->GetNode()].Paths();
357 for (PathList::iterator i = node_paths.begin(); i != node_paths.end(); ++i) {
358 if (*i == cycle_begin) {
359 node_paths.erase(i);
360 node_paths.push_back(cycle_begin);
361 break;
362 }
363 }
364 }
365 cycle_begin = path[prev];
366 Edge edge = this->job[prev][cycle_begin->GetNode()];
367 edge.RemoveFlow(flow);
368 } while (cycle_begin != cycle_end);
369 }
370
380 bool MCF1stPass::EliminateCycles(PathVector &path, NodeID origin_id, NodeID next_id)
381 {
382 static Path *invalid_path = new Path(INVALID_NODE, true);
383 Path *at_next_pos = path[next_id];
384
385 /* this node has already been searched */
386 if (at_next_pos == invalid_path) return false;
387
388 if (at_next_pos == NULL) {
389 /* Summarize paths; add up the paths with the same source and next hop
390 * in one path each. */
391 PathList &paths = this->job[next_id].Paths();
392 PathViaMap next_hops;
393 for (PathList::iterator i = paths.begin(); i != paths.end();) {
394 Path *new_child = *i;
395 uint new_flow = new_child->GetFlow();
396 if (new_flow == 0) break;
397 if (new_child->GetOrigin() == origin_id) {
398 PathViaMap::iterator via_it = next_hops.find(new_child->GetNode());
399 if (via_it == next_hops.end()) {
400 next_hops[new_child->GetNode()] = new_child;
401 ++i;
402 } else {
403 Path *child = via_it->second;
404 child->AddFlow(new_flow);
405 new_child->ReduceFlow(new_flow);
406
407 /* We might hit end() with with the ++ here and skip the
408 * newly push_back'ed path. That's good as the flow of that
409 * path is 0 anyway. */
410 paths.erase(i++);
411 paths.push_back(new_child);
412 }
413 } else {
414 ++i;
415 }
416 }
417 bool found = false;
418 /* Search the next hops for nodes we have already visited */
419 for (PathViaMap::iterator via_it = next_hops.begin();
420 via_it != next_hops.end(); ++via_it) {
421 Path *child = via_it->second;
422 if (child->GetFlow() > 0) {
423 /* Push one child into the path vector and search this child's
424 * children. */
425 path[next_id] = child;
426 found = this->EliminateCycles(path, origin_id, child->GetNode()) || found;
427 }
428 }
429 /* All paths departing from this node have been searched. Mark as
430 * resolved if no cycles found. If cycles were found further cycles
431 * could be found in this branch, thus it has to be searched again next
432 * time we spot it.
433 */
434 path[next_id] = found ? NULL : invalid_path;
435 return found;
436 }
437
438 /* This node has already been visited => we have a cycle.
439 * Backtrack to find the exact flow. */
440 uint flow = this->FindCycleFlow(path, at_next_pos);
441 if (flow > 0) {
442 this->EliminateCycle(path, at_next_pos, flow);
443 return true;
444 }
445
446 return false;
447 }
448
454 bool MCF1stPass::EliminateCycles()
455 {
456 bool cycles_found = false;
457 uint size = this->job.Size();
458 PathVector path(size, NULL);
459 for (NodeID node = 0; node < size; ++node) {
460 /* Starting at each node in the graph find all cycles involving this
461 * node. */
462 std::fill(path.begin(), path.end(), (Path *)NULL);
463 cycles_found |= this->EliminateCycles(path, node, node);
464 }
465 return cycles_found;
466 }
467
472 MCF1stPass::MCF1stPass(LinkGraphJob &job) : MultiCommodityFlow(job)
473 {
474 PathVector paths;
475 uint size = job.Size();
476 uint accuracy = job.Settings().accuracy;
477 bool more_loops;
478
479 do {
480 more_loops = false;
481 for (NodeID source = 0; source < size; ++source) {
482 /* First saturate the shortest paths. */
483 this->Dijkstra<DistanceAnnotation, GraphEdgeIterator>(source, paths);
484
485 for (NodeID dest = 0; dest < size; ++dest) {
486 Edge edge = job[source][dest];
487 if (edge.UnsatisfiedDemand() > 0) {
488 Path *path = paths[dest];
489 assert(path != NULL);
490 /* Generally only allow paths that don't exceed the
491 * available capacity. But if no demand has been assigned
492 * yet, make an exception and allow any valid path *once*. */
493 if (path->GetFreeCapacity() > 0 && this->PushFlow(edge, path,
494 accuracy, this->max_saturation) > 0) {
495 /* If a path has been found there is a chance we can
496 * find more. */
497 more_loops = more_loops || (edge.UnsatisfiedDemand() > 0);
498 } else if (edge.UnsatisfiedDemand() == edge.Demand() &&
499 path->GetFreeCapacity() > INT_MIN) {
500 this->PushFlow(edge, path, accuracy, UINT_MAX);
501 }
502 }
503 }
504 this->CleanupPaths(source, paths);
505 }
506 } while (more_loops || this->EliminateCycles());
507 }
508
514 MCF2ndPass::MCF2ndPass(LinkGraphJob &job) : MultiCommodityFlow(job)
515 {
516 this->max_saturation = UINT_MAX; // disable artificial cap on saturation
517 PathVector paths;
518 uint size = job.Size();
519 uint accuracy = job.Settings().accuracy;
520 bool demand_left = true;
521 while (demand_left) {
522 demand_left = false;
523 for (NodeID source = 0; source < size; ++source) {
524 this->Dijkstra<CapacityAnnotation, FlowEdgeIterator>(source, paths);
525 for (NodeID dest = 0; dest < size; ++dest) {
526 Edge edge = this->job[source][dest];
527 Path *path = paths[dest];
528 if (edge.UnsatisfiedDemand() > 0 && path->GetFreeCapacity() > INT_MIN) {
529 this->PushFlow(edge, path, accuracy, UINT_MAX);
530 if (edge.UnsatisfiedDemand() > 0) demand_left = true;
531 }
532 }
533 this->CleanupPaths(source, paths);
534 }
535 }
536 }
537
549 template <typename T>
550 bool Greater(T x_anno, T y_anno, NodeID x, NodeID y)
551 {
552 if (x_anno > y_anno) return true;
553 if (x_anno < y_anno) return false;
554 return x > y;
555 }
556
563 bool CapacityAnnotation::Comparator::operator()(const CapacityAnnotation *x,
564 const CapacityAnnotation *y) const
565 {
566 return x != y && Greater<int>(x->GetAnnotation(), y->GetAnnotation(),
567 x->GetNode(), y->GetNode());
568 }
569
576 bool DistanceAnnotation::Comparator::operator()(const DistanceAnnotation *x,
577 const DistanceAnnotation *y) const
578 {
579 return x != y && !Greater<uint>(x->GetAnnotation(), y->GetAnnotation(),
580 x->GetNode(), y->GetNode());
581 }