# mcf.cpp

Go to the documentation of this file.

    00001 /* $Id$ */
    00002
    00003 /*
    00004  * This file is part of OpenTTD.
    00005  * 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.
    00006  * 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.
    00007  * 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 <http://www.gnu.org/licenses/>.
    00008  */
    00009
    00012 #include "mcf.h"
    00013 #include "../core/math_func.hpp"
    00014
    00024 bool DistanceAnnotation::IsBetter(const DistanceAnnotation *base, uint cap,
    00025     int free_cap, uint dist) const
    00026 {
    00027   /* If any of the paths is disconnected, the other one is better. If both
    00028    * are disconnected, this path is better.
    00029    */
    00030   if (base->distance == UINT_MAX) {
    00031     return false;
    00032   } else if (this->distance == UINT_MAX) {
    00033     return true;
    00034   }
    00035
    00036   if (free_cap > 0 && base->free_capacity > 0) {
    00037     /* If both paths have capacity left, compare their distances.
    00038      * If the other path has capacity left and this one hasn't, the
    00039      * other one's better.
    00040      */
    00041     return this->free_capacity > 0 ? (base->distance + dist < this->distance) : true;
    00042   } else {
    00043     /* If the other path doesn't have capacity left, but this one has,
    00044      * this one is better.
    00045      * If both paths are out of capacity, do the regular distance
    00046      * comparison.
    00047      */
    00048     return this->free_capacity > 0 ? false : (base->distance + dist < this->distance);
    00049   }
    00050 }
    00051
    00061 bool CapacityAnnotation::IsBetter(const CapacityAnnotation *base, uint cap,
    00062     int free_cap, uint dist) const
    00063 {
    00064   int min_cap = (min(base->free_capacity, free_cap) << 4) / (min(base->capacity, cap) + 1);
    00065   int this_cap = this->GetCapacityRatio();
    00066   if (min_cap == this_cap) {
    00067     /* If the capacities are the same and the other path isn't disconnected
    00068      * choose the shorter path.
    00069      */
    00070     return base->distance == UINT_MAX ? false : (base->distance + dist < this->distance);
    00071   } else {
    00072     return min_cap > this_cap;
    00073   }
    00074 }
    00075
    00089 template<class Tannotation>
    00090 void MultiCommodityFlow::Dijkstra(NodeID source_node, PathVector &paths,
    00091     bool create_new_paths)
    00092 {
    00093   typedef std::set<Tannotation *, typename Tannotation::comp> AnnoSet;
    00094   uint size = this->graph->GetSize();
    00095   StationID source_station = this->graph->GetNode(source_node).station;
    00096   AnnoSet annos;
    00097   paths.resize(size, NULL);
    00098   for (NodeID node = 0; node < size; ++node) {
    00099     Tannotation *anno = new Tannotation(node, node == source_node);
    00100     annos.insert(anno);
    00101     paths[node] = anno;
    00102   }
    00103   while(!annos.empty()) {
    00104     typename AnnoSet::iterator i = annos.begin();
    00105     Tannotation *source = *i;
    00106     annos.erase(i);
    00107     NodeID from = source->GetNode();
    00108     NodeID to = this->graph->GetFirstEdge(from);
    00109     while (to != INVALID_NODE) {
    00110       Edge &edge = this->graph->GetEdge(from, to);
    00111       assert(edge.distance < UINT_MAX);
    00112       if (create_new_paths ||
    00113           this->graph->GetNode(from).flows[source_station]
    00114           [this->graph->GetNode(to).station] > 0) {
    00115         uint capacity = edge.capacity;
    00116         if (create_new_paths) {
    00117           capacity *= this->graph->GetSettings().short_path_saturation;
    00118           capacity /= 100;
    00119           if (capacity == 0) capacity = 1;
    00120         }
    00121         /* punish in-between stops a little */
    00122         uint distance = edge.distance + 1;
    00123         Tannotation *dest = static_cast<Tannotation *>(paths[to]);
    00124         if (dest->IsBetter(source, capacity, capacity - edge.flow, distance)) {
    00125           annos.erase(dest);
    00126           dest->Fork(source, capacity, capacity - edge.flow, distance);
    00127           annos.insert(dest);
    00128         }
    00129       }
    00130       to = edge.next_edge;
    00131     }
    00132   }
    00133 }
    00134
    00140 void MultiCommodityFlow::CleanupPaths(NodeID source_id, PathVector &paths) {
    00141   Path *source = paths[source_id];
    00142   paths[source_id] = NULL;
    00143   for(PathVector::iterator i = paths.begin(); i != paths.end(); ++i) {
    00144     Path *path = *i;
    00145     if (path != NULL) {
    00146       if (path->GetParent() == source) path->UnFork();
    00147       while (path != source && path != NULL && path->GetFlow() == 0) {
    00148         Path *parent = path->GetParent();
    00149         path->UnFork();
    00150         if (path->GetNumChildren() == 0) {
    00151           paths[path->GetNode()] = NULL;
    00152           delete path;
    00153         }
    00154         path = parent;
    00155       }
    00156     }
    00157   }
    00158   delete source;
    00159   paths.clear();
    00160 }
    00161
    00171 uint MultiCommodityFlow::PushFlow(Edge &edge, Path *path, uint accuracy,
    00172     bool positive_cap)
    00173 {
    00174   assert(edge.unsatisfied_demand > 0);
    00175   uint flow = Clamp(edge.demand / accuracy, 1, edge.unsatisfied_demand);
    00176   flow = path->AddFlow(flow, this->graph, positive_cap);
    00177   edge.unsatisfied_demand -= flow;
    00178   return flow;
    00179 }
    00180
    00187 uint MCF1stPass::FindCycleFlow(const PathVector &path, const Path *cycle_begin)
    00188 {
    00189   uint flow = UINT_MAX;
    00190   const Path *cycle_end = cycle_begin;
    00191   do {
    00192     flow = min(flow, cycle_begin->GetFlow());
    00193     cycle_begin = path[cycle_begin->GetNode()];
    00194   } while(cycle_begin != cycle_end);
    00195   return flow;
    00196 }
    00197
    00204 void MCF1stPass::EliminateCycle(PathVector &path, Path *cycle_begin, uint flow)
    00205 {
    00206   Path *cycle_end = cycle_begin;
    00207   do {
    00208     NodeID prev = cycle_begin->GetNode();
    00209     cycle_begin->ReduceFlow(flow);
    00210     cycle_begin = path[cycle_begin->GetNode()];
    00211     Edge &edge = this->graph->GetEdge(prev, cycle_begin->GetNode());
    00212     edge.flow -= flow;
    00213   } while(cycle_begin != cycle_end);
    00214 }
    00215
    00225 bool MCF1stPass::EliminateCycles(PathVector &path, NodeID origin_id, NodeID next_id)
    00226 {
    00227   static Path *invalid_path = new Path(INVALID_NODE, true);
    00228   Path *at_next_pos = path[next_id];
    00229   if (at_next_pos == invalid_path) {
    00230     /* this node has already been searched */
    00231     return false;
    00232   } else if (at_next_pos == NULL) {
    00233     /* summarize paths; add up the paths with the same source and next hop
    00234      * in one path each
    00235      */
    00236     PathSet &paths = this->graph->GetNode(next_id).paths;
    00237     PathViaMap next_hops;
    00238     for(PathSet::iterator i = paths.begin(); i != paths.end(); ++i) {
    00239       Path *new_child = *i;
    00240       if (new_child->GetOrigin() == origin_id) {
    00241         PathViaMap::iterator via_it = next_hops.find(new_child->GetNode());
    00242         if (via_it == next_hops.end()) {
    00243           next_hops[new_child->GetNode()] = new_child;
    00244         } else {
    00245           Path *child = via_it->second;
    00246           uint new_flow = new_child->GetFlow();
    00247           child->AddFlow(new_flow);
    00248           new_child->ReduceFlow(new_flow);
    00249         }
    00250       }
    00251     }
    00252     bool found = false;
    00253     /* search the next hops for nodes we have already visited */
    00254     for (PathViaMap::iterator via_it = next_hops.begin();
    00255         via_it != next_hops.end(); ++via_it)
    00256     {
    00257       Path *child = via_it->second;
    00258       if (child->GetFlow() > 0) {
    00259         /* push one child into the path vector and search this child's
    00260          * children
    00261          */
    00262         path[next_id] = child;
    00263         found = this->EliminateCycles(path, origin_id, child->GetNode()) || found;
    00264       }
    00265     }
    00266     /* All paths departing from this node have been searched. Mark as
    00267      * resolved if no cycles found. If cycles were found further cycles
    00268      * could be found in this branch, thus it has to be searched again next
    00269      * time we spot it.
    00270      */
    00271     path[next_id] = found ? NULL : invalid_path;
    00272     return found;
    00273   } else {
    00274     /* this node has already been visited => we have a cycle
    00275      * backtrack to find the exact flow
    00276      */
    00277     uint flow = this->FindCycleFlow(path, at_next_pos);
    00278     if (flow > 0) {
    00279       this->EliminateCycle(path, at_next_pos, flow);
    00280       return true;
    00281     } else {
    00282       return false;
    00283     }
    00284   }
    00285 }
    00286
    00292 bool MCF1stPass::EliminateCycles()
    00293 {
    00294   bool cycles_found = false;
    00295   uint size = this->graph->GetSize();
    00296   PathVector path(size, NULL);
    00297   for (NodeID node = 0; node < size; ++node) {
    00298     /* starting at each node in the graph find all cycles involving this node */
    00299     std::fill(path.begin(), path.end(), (Path *)NULL);
    00300     cycles_found = this->EliminateCycles(path, node, node) || cycles_found;
    00301   }
    00302   return cycles_found;
    00303 }
    00304
    00309 MCF1stPass::MCF1stPass(LinkGraphComponent *graph) : MultiCommodityFlow(graph)
    00310 {
    00311   PathVector paths;
    00312   uint size = this->graph->GetSize();
    00313   uint accuracy = this->graph->GetSettings().accuracy;
    00314   bool no_overload = this->graph->GetSettings().no_overload_links;
    00315   bool more_loops = true;
    00316
    00317   while (more_loops) {
    00318     more_loops = false;
    00319
    00320     for (NodeID source = 0; source < size; ++source) {
    00321       /* first saturate the shortest paths */
    00322       this->Dijkstra<DistanceAnnotation>(source, paths, true);
    00323
    00324       for (NodeID dest = 0; dest < size; ++dest) {
    00325         Edge &edge = this->graph->GetEdge(source, dest);
    00326         if (edge.unsatisfied_demand > 0) {
    00327           Path *path = paths[dest];
    00328           assert(path != NULL);
    00329           /* Generally only allow paths that don't exceed the available capacity.
    00330            * But if no demand has been assigned yet, make an exception and allow
    00331            * any valid path *once*.
    00332            */
    00333           if (path->GetFreeCapacity() > 0 && this->PushFlow(edge, path,
    00334               accuracy, no_overload) > 0) {
    00335             /* if a path has been found there is a chance we can
    00336              * find more
    00337              */
    00338             more_loops = (edge.unsatisfied_demand > 0);
    00339           } else if (edge.unsatisfied_demand == edge.demand && path->GetFreeCapacity() > INT_MIN) {
    00340             this->PushFlow(edge, path, accuracy, false);
    00341           }
    00342         }
    00343       }
    00344       CleanupPaths(source, paths);
    00345     }
    00346     if (!more_loops) more_loops = EliminateCycles();
    00347   }
    00348 }
    00349
    00354 MCF2ndPass::MCF2ndPass(LinkGraphComponent *graph) : MultiCommodityFlow(graph)
    00355 {
    00356   PathVector paths;
    00357   uint size = this->graph->GetSize();
    00358   uint accuracy = this->graph->GetSettings().accuracy;
    00359   bool no_overload = this->graph->GetSettings().no_overload_links;
    00360   bool demand_left = true;
    00361   uint flow_pushed = 1;
    00362   while (demand_left && flow_pushed > 0) {
    00363     flow_pushed = 0;
    00364     demand_left = false;
    00365     for (NodeID source = 0; source < size; ++source) {
    00366       /* Then assign all remaining demands */
    00367       this->Dijkstra<CapacityAnnotation>(source, paths, false);
    00368       for (NodeID dest = 0; dest < size; ++dest) {
    00369         Edge &edge = this->graph->GetEdge(source, dest);
    00370         Path *path = paths[dest];
    00371         if (edge.unsatisfied_demand > 0 && path->GetFreeCapacity() > INT_MIN) {
    00372           flow_pushed += this->PushFlow(edge, path, accuracy, no_overload);
    00373           if (edge.unsatisfied_demand > 0) demand_left = true;
    00374         }
    00375       }
    00376       CleanupPaths(source, paths);
    00377     }
    00378   }
    00379 }
    00380
    00392 template <typename T>
    00393 bool greater(T x_anno, T y_anno, NodeID x, NodeID y) {
    00394   if (x_anno > y_anno) {
    00395     return true;
    00396   } else if (x_anno < y_anno) {
    00397     return false;
    00398   } else {
    00399     return x > y;
    00400   }
    00401 }
    00402
    00409 bool CapacityAnnotation::comp::operator()(const CapacityAnnotation *x,
    00410     const CapacityAnnotation *y) const
    00411 {
    00412   return x != y && greater<int>(x->GetAnnotation(), y->GetAnnotation(),
    00413       x->GetNode(), y->GetNode());
    00414 }
    00415
    00422 bool DistanceAnnotation::comp::operator()(const DistanceAnnotation *x,
    00423     const DistanceAnnotation *y) const
    00424 {
    00425   return x != y && !greater<uint>(x->GetAnnotation(), y->GetAnnotation(),
    00426       x->GetNode(), y->GetNode());
    00427 }