1 /** @file mcf.cpp Definition of Multi-Commodity-Flow solver. */
4 #include "../core/math_func.hpp"
5 #include "../timer/timer_game_tick.h"
8 #include "../safeguards.h"
10 typedef std::map
<NodeID
, Path
*> PathViaMap
;
13 * Distance-based annotation for use in the Dijkstra algorithm. This is close
14 * to the original meaning of "annotation" in this context. Paths are rated
15 * according to the sum of distances of their edges.
17 class DistanceAnnotation
: public Path
{
22 * @param n ID of node to be annotated.
23 * @param source If the node is the source of its path.
25 DistanceAnnotation(NodeID n
, bool source
= false) : Path(n
, source
) {}
27 bool IsBetter(const DistanceAnnotation
*base
, uint cap
, int free_cap
, uint dist
) const;
30 * Return the actual value of the annotation, in this case the distance.
33 inline uint
GetAnnotation() const { return this->distance
; }
36 * Update the cached annotation value
38 inline void UpdateAnnotation() { }
41 * Comparator for std containers.
44 bool operator()(const DistanceAnnotation
*x
, const DistanceAnnotation
*y
) const;
49 * Capacity-based annotation for use in the Dijkstra algorithm. This annotation
50 * rates paths according to the maximum capacity of their edges. The Dijkstra
51 * algorithm still gives meaningful results like this as the capacity of a path
52 * can only decrease or stay the same if you add more edges.
54 class CapacityAnnotation
: public Path
{
55 int cached_annotation
;
61 * @param n ID of node to be annotated.
62 * @param source If the node is the source of its path.
64 CapacityAnnotation(NodeID n
, bool source
= false) : Path(n
, source
) {}
66 bool IsBetter(const CapacityAnnotation
*base
, uint cap
, int free_cap
, uint dist
) const;
69 * Return the actual value of the annotation, in this case the capacity.
72 inline int GetAnnotation() const { return this->cached_annotation
; }
75 * Update the cached annotation value
77 inline void UpdateAnnotation()
79 this->cached_annotation
= this->GetCapacityRatio();
83 * Comparator for std containers.
86 bool operator()(const CapacityAnnotation
*x
, const CapacityAnnotation
*y
) const;
91 * Iterator class for getting the edges in the order of their next_edge
94 class GraphEdgeIterator
{
96 LinkGraphJob
&job
; ///< Job being executed
98 std::vector
<LinkGraphJob::EdgeAnnotation
>::const_iterator i
; ///< Iterator pointing to current edge.
99 std::vector
<LinkGraphJob::EdgeAnnotation
>::const_iterator end
; ///< Iterator pointing beyond last edge.
104 * Construct a GraphEdgeIterator.
105 * @param job Job to iterate on.
107 GraphEdgeIterator(LinkGraphJob
&job
) : job(job
), i(), end() {}
110 * Setup the node to start iterating at.
111 * @param node Node to start iterating at.
113 void SetNode(NodeID
, NodeID node
)
115 this->i
= this->job
[node
].edges
.cbegin();
116 this->end
= this->job
[node
].edges
.cend();
120 * Retrieve the ID of the node the next edge points to.
121 * @return Next edge's target node ID or INVALID_NODE.
125 return this->i
!= this->end
? (this->i
++)->base
.dest_node
: INVALID_NODE
;
130 * Iterator class for getting edges from a FlowStatMap.
132 class FlowEdgeIterator
{
134 LinkGraphJob
&job
; ///< Link graph job we're working with.
136 /** Lookup table for getting NodeIDs from StationIDs. */
137 std::vector
<NodeID
> station_to_node
;
139 /** Current iterator in the shares map. */
140 FlowStat::SharesMap::const_iterator it
;
142 /** End of the shares map. */
143 FlowStat::SharesMap::const_iterator end
;
148 * @param job Link graph job to work with.
150 FlowEdgeIterator(LinkGraphJob
&job
) : job(job
)
152 for (NodeID i
= 0; i
< job
.Size(); ++i
) {
153 StationID st
= job
[i
].base
.station
;
154 if (st
>= this->station_to_node
.size()) {
155 this->station_to_node
.resize(st
+ 1);
157 this->station_to_node
[st
] = i
;
162 * Setup the node to retrieve edges from.
163 * @param source Root of the current path tree.
164 * @param node Current node to be checked for outgoing flows.
166 void SetNode(NodeID source
, NodeID node
)
168 const FlowStatMap
&flows
= this->job
[node
].flows
;
169 FlowStatMap::const_iterator it
= flows
.find(this->job
[source
].base
.station
);
170 if (it
!= flows
.end()) {
171 this->it
= it
->second
.GetShares()->begin();
172 this->end
= it
->second
.GetShares()->end();
174 this->it
= FlowStat::empty_sharesmap
.begin();
175 this->end
= FlowStat::empty_sharesmap
.end();
180 * Get the next node for which a flow exists.
181 * @return ID of next node with flow.
185 if (this->it
== this->end
) return INVALID_NODE
;
186 return this->station_to_node
[(this->it
++)->second
];
191 * Determines if an extension to the given Path with the given parameters is
192 * better than this path.
193 * @param base Other path.
194 * @param free_cap Capacity of the new edge to be added to base.
195 * @param dist Distance of the new edge.
196 * @return True if base + the new edge would be better than the path associated
197 * with this annotation.
199 bool DistanceAnnotation::IsBetter(const DistanceAnnotation
*base
, uint
,
200 int free_cap
, uint dist
) const
202 /* If any of the paths is disconnected, the other one is better. If both
203 * are disconnected, this path is better.*/
204 if (base
->distance
== UINT_MAX
) {
206 } else if (this->distance
== UINT_MAX
) {
210 if (free_cap
> 0 && base
->free_capacity
> 0) {
211 /* If both paths have capacity left, compare their distances.
212 * If the other path has capacity left and this one hasn't, the
213 * other one's better (thus, return true). */
214 return this->free_capacity
> 0 ? (base
->distance
+ dist
< this->distance
) : true;
216 /* If the other path doesn't have capacity left, but this one has,
217 * the other one is worse (thus, return false).
218 * If both paths are out of capacity, do the regular distance
220 return this->free_capacity
> 0 ? false : (base
->distance
+ dist
< this->distance
);
225 * Determines if an extension to the given Path with the given parameters is
226 * better than this path.
227 * @param base Other path.
228 * @param free_cap Capacity of the new edge to be added to base.
229 * @param dist Distance of the new edge.
230 * @return True if base + the new edge would be better than the path associated
231 * with this annotation.
233 bool CapacityAnnotation::IsBetter(const CapacityAnnotation
*base
, uint cap
,
234 int free_cap
, uint dist
) const
236 int min_cap
= Path::GetCapacityRatio(std::min(base
->free_capacity
, free_cap
), std::min(base
->capacity
, cap
));
237 int this_cap
= this->GetCapacityRatio();
238 if (min_cap
== this_cap
) {
239 /* If the capacities are the same and the other path isn't disconnected
240 * choose the shorter path. */
241 return base
->distance
== UINT_MAX
? false : (base
->distance
+ dist
< this->distance
);
243 return min_cap
> this_cap
;
248 * A slightly modified Dijkstra algorithm. Grades the paths not necessarily by
249 * distance, but by the value Tannotation computes. It uses the max_saturation
250 * setting to artificially decrease capacities.
251 * @tparam Tannotation Annotation to be used.
252 * @tparam Tedge_iterator Iterator to be used for getting outgoing edges.
253 * @param source_node Node where the algorithm starts.
254 * @param paths Container for the paths to be calculated.
256 template <class Tannotation
, class Tedge_iterator
>
257 void MultiCommodityFlow::Dijkstra(NodeID source_node
, PathVector
&paths
)
259 typedef std::set
<Tannotation
*, typename
Tannotation::Comparator
> AnnoSet
;
260 Tedge_iterator
iter(this->job
);
261 uint16_t size
= this->job
.Size();
263 paths
.resize(size
, nullptr);
264 for (NodeID node
= 0; node
< size
; ++node
) {
265 Tannotation
*anno
= new Tannotation(node
, node
== source_node
);
266 anno
->UpdateAnnotation();
270 while (!annos
.empty()) {
271 typename
AnnoSet::iterator i
= annos
.begin();
272 Tannotation
*source
= *i
;
274 NodeID from
= source
->GetNode();
275 iter
.SetNode(source_node
, from
);
276 for (NodeID to
= iter
.Next(); to
!= INVALID_NODE
; to
= iter
.Next()) {
277 if (to
== from
) continue; // Not a real edge but a consumption sign.
278 const Edge
&edge
= this->job
[from
][to
];
279 uint capacity
= edge
.base
.capacity
;
280 if (this->max_saturation
!= UINT_MAX
) {
281 capacity
*= this->max_saturation
;
283 if (capacity
== 0) capacity
= 1;
285 /* Prioritize the fastest route for passengers, mail and express cargo,
286 * and the shortest route for other classes of cargo.
287 * In-between stops are punished with a 1 tile or 1 day penalty. */
288 bool express
= IsCargoInClass(this->job
.Cargo(), CC_PASSENGERS
) ||
289 IsCargoInClass(this->job
.Cargo(), CC_MAIL
) ||
290 IsCargoInClass(this->job
.Cargo(), CC_EXPRESS
);
291 uint distance
= DistanceMaxPlusManhattan(this->job
[from
].base
.xy
, this->job
[to
].base
.xy
) + 1;
292 /* Compute a default travel time from the distance and an average speed of 1 tile/day. */
293 uint time
= (edge
.base
.TravelTime() != 0) ? edge
.base
.TravelTime() + Ticks::DAY_TICKS
: distance
* Ticks::DAY_TICKS
;
294 uint distance_anno
= express
? time
: distance
;
296 Tannotation
*dest
= static_cast<Tannotation
*>(paths
[to
]);
297 if (dest
->IsBetter(source
, capacity
, capacity
- edge
.Flow(), distance_anno
)) {
299 dest
->Fork(source
, capacity
, capacity
- edge
.Flow(), distance_anno
);
300 dest
->UpdateAnnotation();
308 * Clean up paths that lead nowhere and the root path.
309 * @param source_id ID of the root node.
310 * @param paths Paths to be cleaned up.
312 void MultiCommodityFlow::CleanupPaths(NodeID source_id
, PathVector
&paths
)
314 Path
*source
= paths
[source_id
];
315 paths
[source_id
] = nullptr;
316 for (PathVector::iterator i
= paths
.begin(); i
!= paths
.end(); ++i
) {
318 if (path
== nullptr) continue;
319 if (path
->GetParent() == source
) path
->Detach();
320 while (path
!= source
&& path
!= nullptr && path
->GetFlow() == 0) {
321 Path
*parent
= path
->GetParent();
323 if (path
->GetNumChildren() == 0) {
324 paths
[path
->GetNode()] = nullptr;
335 * Push flow along a path and update the unsatisfied_demand of the associated
337 * @param node Node where the path starts.
338 * @param to Node where the path ends.
339 * @param path End of the path the flow should be pushed on.
340 * @param accuracy Accuracy of the calculation.
341 * @param max_saturation If < UINT_MAX only push flow up to the given
342 * saturation, otherwise the path can be "overloaded".
344 uint
MultiCommodityFlow::PushFlow(Node
&node
, NodeID to
, Path
*path
, uint accuracy
,
347 assert(node
.UnsatisfiedDemandTo(to
) > 0);
348 uint flow
= Clamp(node
.DemandTo(to
) / accuracy
, 1, node
.UnsatisfiedDemandTo(to
));
349 flow
= path
->AddFlow(flow
, this->job
, max_saturation
);
350 node
.SatisfyDemandTo(to
, flow
);
355 * Find the flow along a cycle including cycle_begin in path.
356 * @param path Set of paths that form the cycle.
357 * @param cycle_begin Path to start at.
358 * @return Flow along the cycle.
360 uint
MCF1stPass::FindCycleFlow(const PathVector
&path
, const Path
*cycle_begin
)
362 uint flow
= UINT_MAX
;
363 const Path
*cycle_end
= cycle_begin
;
365 flow
= std::min(flow
, cycle_begin
->GetFlow());
366 cycle_begin
= path
[cycle_begin
->GetNode()];
367 } while (cycle_begin
!= cycle_end
);
372 * Eliminate a cycle of the given flow in the given set of paths.
373 * @param path Set of paths containing the cycle.
374 * @param cycle_begin Part of the cycle to start at.
375 * @param flow Flow along the cycle.
377 void MCF1stPass::EliminateCycle(PathVector
&path
, Path
*cycle_begin
, uint flow
)
379 Path
*cycle_end
= cycle_begin
;
381 NodeID prev
= cycle_begin
->GetNode();
382 cycle_begin
->ReduceFlow(flow
);
383 if (cycle_begin
->GetFlow() == 0) {
384 PathList
&node_paths
= this->job
[cycle_begin
->GetParent()->GetNode()].paths
;
385 for (PathList::iterator i
= node_paths
.begin(); i
!= node_paths
.end(); ++i
) {
386 if (*i
== cycle_begin
) {
388 node_paths
.push_back(cycle_begin
);
393 cycle_begin
= path
[prev
];
394 Edge
&edge
= this->job
[prev
][cycle_begin
->GetNode()];
395 edge
.RemoveFlow(flow
);
396 } while (cycle_begin
!= cycle_end
);
400 * Eliminate cycles for origin_id in the graph. Start searching at next_id and
401 * work recursively. Also "summarize" paths: Add up the flows along parallel
403 * @param path Paths checked in parent calls to this method.
404 * @param origin_id Origin of the paths to be checked.
405 * @param next_id Next node to be checked.
406 * @return If any cycles have been found and eliminated.
408 bool MCF1stPass::EliminateCycles(PathVector
&path
, NodeID origin_id
, NodeID next_id
)
410 Path
*at_next_pos
= path
[next_id
];
412 /* this node has already been searched */
413 if (at_next_pos
== Path::invalid_path
) return false;
415 if (at_next_pos
== nullptr) {
416 /* Summarize paths; add up the paths with the same source and next hop
417 * in one path each. */
418 PathList
&paths
= this->job
[next_id
].paths
;
419 PathViaMap next_hops
;
420 for (PathList::iterator i
= paths
.begin(); i
!= paths
.end();) {
421 Path
*new_child
= *i
;
422 uint new_flow
= new_child
->GetFlow();
423 if (new_flow
== 0) break;
424 if (new_child
->GetOrigin() == origin_id
) {
425 PathViaMap::iterator via_it
= next_hops
.find(new_child
->GetNode());
426 if (via_it
== next_hops
.end()) {
427 next_hops
[new_child
->GetNode()] = new_child
;
430 Path
*child
= via_it
->second
;
431 child
->AddFlow(new_flow
);
432 new_child
->ReduceFlow(new_flow
);
434 /* We might hit end() with with the ++ here and skip the
435 * newly push_back'ed path. That's good as the flow of that
436 * path is 0 anyway. */
438 paths
.push_back(new_child
);
445 /* Search the next hops for nodes we have already visited */
446 for (PathViaMap::iterator via_it
= next_hops
.begin();
447 via_it
!= next_hops
.end(); ++via_it
) {
448 Path
*child
= via_it
->second
;
449 if (child
->GetFlow() > 0) {
450 /* Push one child into the path vector and search this child's
452 path
[next_id
] = child
;
453 found
= this->EliminateCycles(path
, origin_id
, child
->GetNode()) || found
;
456 /* All paths departing from this node have been searched. Mark as
457 * resolved if no cycles found. If cycles were found further cycles
458 * could be found in this branch, thus it has to be searched again next
461 path
[next_id
] = found
? nullptr : Path::invalid_path
;
465 /* This node has already been visited => we have a cycle.
466 * Backtrack to find the exact flow. */
467 uint flow
= this->FindCycleFlow(path
, at_next_pos
);
469 this->EliminateCycle(path
, at_next_pos
, flow
);
477 * Eliminate all cycles in the graph. Check paths starting at each node for
479 * @return If any cycles have been found and eliminated.
481 bool MCF1stPass::EliminateCycles()
483 bool cycles_found
= false;
484 uint16_t size
= this->job
.Size();
485 PathVector
path(size
, nullptr);
486 for (NodeID node
= 0; node
< size
; ++node
) {
487 /* Starting at each node in the graph find all cycles involving this
489 std::fill(path
.begin(), path
.end(), (Path
*)nullptr);
490 cycles_found
|= this->EliminateCycles(path
, node
, node
);
496 * Run the first pass of the MCF calculation.
497 * @param job Link graph job to calculate.
499 MCF1stPass::MCF1stPass(LinkGraphJob
&job
) : MultiCommodityFlow(job
)
502 uint16_t size
= job
.Size();
503 uint accuracy
= job
.Settings().accuracy
;
505 std::vector
<bool> finished_sources(size
);
509 for (NodeID source
= 0; source
< size
; ++source
) {
510 if (finished_sources
[source
]) continue;
512 /* First saturate the shortest paths. */
513 this->Dijkstra
<DistanceAnnotation
, GraphEdgeIterator
>(source
, paths
);
515 Node
&src_node
= job
[source
];
516 bool source_demand_left
= false;
517 for (NodeID dest
= 0; dest
< size
; ++dest
) {
518 if (src_node
.UnsatisfiedDemandTo(dest
) > 0) {
519 Path
*path
= paths
[dest
];
520 assert(path
!= nullptr);
521 /* Generally only allow paths that don't exceed the
522 * available capacity. But if no demand has been assigned
523 * yet, make an exception and allow any valid path *once*. */
524 if (path
->GetFreeCapacity() > 0 && this->PushFlow(src_node
, dest
, path
,
525 accuracy
, this->max_saturation
) > 0) {
526 /* If a path has been found there is a chance we can
528 more_loops
= more_loops
|| (src_node
.UnsatisfiedDemandTo(dest
) > 0);
529 } else if (src_node
.UnsatisfiedDemandTo(dest
) == src_node
.DemandTo(dest
) &&
530 path
->GetFreeCapacity() > INT_MIN
) {
531 this->PushFlow(src_node
, dest
, path
, accuracy
, UINT_MAX
);
533 if (src_node
.UnsatisfiedDemandTo(dest
) > 0) source_demand_left
= true;
536 finished_sources
[source
] = !source_demand_left
;
537 this->CleanupPaths(source
, paths
);
539 } while ((more_loops
|| this->EliminateCycles()) && !job
.IsJobAborted());
543 * Run the second pass of the MCF calculation which assigns all remaining
544 * demands to existing paths.
545 * @param job Link graph job to calculate.
547 MCF2ndPass::MCF2ndPass(LinkGraphJob
&job
) : MultiCommodityFlow(job
)
549 this->max_saturation
= UINT_MAX
; // disable artificial cap on saturation
551 uint16_t size
= job
.Size();
552 uint accuracy
= job
.Settings().accuracy
;
553 bool demand_left
= true;
554 std::vector
<bool> finished_sources(size
);
555 while (demand_left
&& !job
.IsJobAborted()) {
557 for (NodeID source
= 0; source
< size
; ++source
) {
558 if (finished_sources
[source
]) continue;
560 this->Dijkstra
<CapacityAnnotation
, FlowEdgeIterator
>(source
, paths
);
562 Node
&src_node
= job
[source
];
563 bool source_demand_left
= false;
564 for (NodeID dest
= 0; dest
< size
; ++dest
) {
565 Path
*path
= paths
[dest
];
566 if (src_node
.UnsatisfiedDemandTo(dest
) > 0 && path
->GetFreeCapacity() > INT_MIN
) {
567 this->PushFlow(src_node
, dest
, path
, accuracy
, UINT_MAX
);
568 if (src_node
.UnsatisfiedDemandTo(dest
) > 0) {
570 source_demand_left
= true;
574 finished_sources
[source
] = !source_demand_left
;
575 this->CleanupPaths(source
, paths
);
581 * Relation that creates a weak order without duplicates.
582 * Avoid accidentally deleting different paths of the same capacity/distance in
583 * a set. When the annotation is the same node IDs are compared, so there are
585 * @tparam T Type to be compared on.
586 * @param x_anno First value.
587 * @param y_anno Second value.
588 * @param x Node id associated with the first value.
589 * @param y Node id associated with the second value.
591 template <typename T
>
592 bool Greater(T x_anno
, T y_anno
, NodeID x
, NodeID y
)
594 if (x_anno
> y_anno
) return true;
595 if (x_anno
< y_anno
) return false;
600 * Compare two capacity annotations.
601 * @param x First capacity annotation.
602 * @param y Second capacity annotation.
603 * @return If x is better than y.
605 bool CapacityAnnotation::Comparator::operator()(const CapacityAnnotation
*x
,
606 const CapacityAnnotation
*y
) const
608 return x
!= y
&& Greater
<int>(x
->GetAnnotation(), y
->GetAnnotation(),
609 x
->GetNode(), y
->GetNode());
613 * Compare two distance annotations.
614 * @param x First distance annotation.
615 * @param y Second distance annotation.
616 * @return If x is better than y.
618 bool DistanceAnnotation::Comparator::operator()(const DistanceAnnotation
*x
,
619 const DistanceAnnotation
*y
) const
621 return x
!= y
&& !Greater
<uint
>(x
->GetAnnotation(), y
->GetAnnotation(),
622 x
->GetNode(), y
->GetNode());