| 1 | #ifndef SSP_HUNGARIAN_H |
|---|
| 2 | #define SSP_HUNGARIAN_H |
|---|
| 3 | |
|---|
| 4 | #include <limits> |
|---|
| 5 | #include <list> |
|---|
| 6 | #include <algorithm> |
|---|
| 7 | #include <assert.h> |
|---|
| 8 | #include <queue> |
|---|
| 9 | |
|---|
| 10 | #include <lemon/core.h> |
|---|
| 11 | #include <lemon/connectivity.h> |
|---|
| 12 | #include <lemon/bin_heap.h> |
|---|
| 13 | |
|---|
| 14 | ///\ingroup matching |
|---|
| 15 | ///\file |
|---|
| 16 | ///\brief Maximum weight matching algorithms in bipartite graphs. |
|---|
| 17 | |
|---|
| 18 | namespace lemon { |
|---|
| 19 | |
|---|
| 20 | /// \ingroup matching |
|---|
| 21 | /// |
|---|
| 22 | /// \brief Maximum weight matching in (sparse) bipartite graphs |
|---|
| 23 | /// |
|---|
| 24 | /// This class implements a successive shortest path algorithm for finding |
|---|
| 25 | /// a maximum weight matching in an undirected bipartite graph. |
|---|
| 26 | /// Let \f$G = (X \cup Y, E)\f$ be an undirected bipartite graph. The |
|---|
| 27 | /// following linear program corresponds to a maximum weight matching |
|---|
| 28 | /// in the graph \f$G\f$. |
|---|
| 29 | /// |
|---|
| 30 | /// \f$\begin{array}{rrcll} \ |
|---|
| 31 | \max & \displaystyle\sum_{(i,j) \in E} c_{ij} x_{ij}\\ \ |
|---|
| 32 | \mbox{s.t.} & \displaystyle\sum_{i \in X} x_{ij} & \leq & 1, \ |
|---|
| 33 | & \forall j \in \{ j^\prime \in Y \mid (i,j^\prime) \in E \}\\ \ |
|---|
| 34 | & \displaystyle\sum_{j \in Y} x_{ij} & \leq & 1, \ |
|---|
| 35 | & \forall i \in \{ i^\prime \in X \mid (i^\prime,j) \in E \}\\ \ |
|---|
| 36 | & x_{ij} & \geq & 0, & \forall (i,j) \in E\\\end{array}\f$ |
|---|
| 37 | /// |
|---|
| 38 | /// where \f$c_{ij}\f$ is the weight of edge \f$(i,j)\f$. The dual problem |
|---|
| 39 | /// is: |
|---|
| 40 | /// |
|---|
| 41 | /// \f$\begin{array}{rrcll}\min & \displaystyle\sum_{v \in X \cup Y} p_v\\ \ |
|---|
| 42 | \mbox{s.t.} & p_i + p_j & \geq & c_{ij}, & \forall (i,j) \in E\\ \ |
|---|
| 43 | & p_v & \geq & 0, & \forall v \in X \cup Y \end{array}\f$ |
|---|
| 44 | /// |
|---|
| 45 | /// A maximum weight matching is constructed by iteratively considering the |
|---|
| 46 | /// vertices in \f$X = \{x_1, \ldots, x_n\}\f$. In every iteration \f$k\f$ |
|---|
| 47 | /// we establish primal and dual complementary slackness for the subgraph |
|---|
| 48 | /// \f$G[X_k \cup Y]\f$ where \f$X_k = \{x_1, \ldots, x_k\}\f$. |
|---|
| 49 | /// So after the final iteration the primal and dual solution will be equal, |
|---|
| 50 | /// and we will thus have a maximum weight matching. The time complexity of |
|---|
| 51 | /// this method is \f$O(n(n + m)\log n)\f$. |
|---|
| 52 | /// |
|---|
| 53 | /// In case the bipartite graph is dense, it is better to use |
|---|
| 54 | /// \ref MaxWeightedDenseBipartiteMatching, which has a time complexity of |
|---|
| 55 | /// \f$O(n^3)\f$. |
|---|
| 56 | /// |
|---|
| 57 | /// \tparam GR The undirected graph type the algorithm runs on. |
|---|
| 58 | /// \tparam WM The type edge weight map. The default type is |
|---|
| 59 | /// \ref concepts::Graph::EdgeMap "GR::EdgeMap<int>". |
|---|
| 60 | #ifdef DOXYGEN |
|---|
| 61 | template <typename GR, typename WM> |
|---|
| 62 | #else |
|---|
| 63 | template <typename GR, |
|---|
| 64 | typename WM = typename GR::template EdgeMap<int> > |
|---|
| 65 | #endif |
|---|
| 66 | class MaxWeightedBipartiteMatching |
|---|
| 67 | { |
|---|
| 68 | // TODO: |
|---|
| 69 | // - deal with orientation (edges need to be oriented from Y to X) |
|---|
| 70 | // (will be resolved when using a specific BpGraph class) |
|---|
| 71 | // - swap X and Y in case |Y| < |X|, results in improved running times |
|---|
| 72 | |
|---|
| 73 | public: |
|---|
| 74 | /// The graph type of the algorithm |
|---|
| 75 | typedef GR Graph; |
|---|
| 76 | /// The type of the edge weight map |
|---|
| 77 | typedef WM WeightMap; |
|---|
| 78 | /// The value type of the edge weights |
|---|
| 79 | typedef typename WeightMap::Value Value; |
|---|
| 80 | /// The type of the matching map |
|---|
| 81 | typedef typename Graph::template NodeMap<typename Graph::Edge> MatchingMap; |
|---|
| 82 | /// The type of a list of nodes |
|---|
| 83 | typedef std::list<typename Graph::Node> NodeList; |
|---|
| 84 | |
|---|
| 85 | private: |
|---|
| 86 | TEMPLATE_GRAPH_TYPEDEFS(Graph); |
|---|
| 87 | |
|---|
| 88 | typedef typename Graph::template NodeMap<bool> BoolMap; |
|---|
| 89 | typedef typename Graph::template NodeMap<Node> AncestorMap; |
|---|
| 90 | typedef typename Graph::template NodeMap<Value> PotMap; |
|---|
| 91 | typedef typename Graph::template NodeMap<Value> DistMap; |
|---|
| 92 | |
|---|
| 93 | typedef typename Graph::template EdgeMap<bool> EdgeBoolMap; |
|---|
| 94 | |
|---|
| 95 | typedef Orienter<const Graph> OrientedGraph; |
|---|
| 96 | typedef typename OrientedGraph::Arc OrientedArc; |
|---|
| 97 | typedef typename OrientedGraph::ArcIt OrientedArcIt; |
|---|
| 98 | typedef typename OrientedGraph::OutArcIt OrientedOutArcIt; |
|---|
| 99 | typedef typename OrientedGraph::InArcIt OrientedInArcIt; |
|---|
| 100 | typedef typename Graph::template NodeMap<OrientedArc> PredMap; |
|---|
| 101 | |
|---|
| 102 | typedef typename Graph::template NodeMap<int> HeapCrossRef; |
|---|
| 103 | typedef BinHeap<Value, HeapCrossRef> Heap; |
|---|
| 104 | |
|---|
| 105 | const Graph& _graph; |
|---|
| 106 | OrientedGraph* _pOrientedGraph; |
|---|
| 107 | |
|---|
| 108 | const WeightMap& _weight; |
|---|
| 109 | BoolMap _bpMap; |
|---|
| 110 | PotMap _pot; |
|---|
| 111 | |
|---|
| 112 | EdgeBoolMap* _pMatching; |
|---|
| 113 | Value _matchingWeight; |
|---|
| 114 | |
|---|
| 115 | MatchingMap* _pMatchingMap; |
|---|
| 116 | int _matchingSize; |
|---|
| 117 | |
|---|
| 118 | NodeList _X; |
|---|
| 119 | NodeList _Y; |
|---|
| 120 | |
|---|
| 121 | bool isFree(const Node& v) |
|---|
| 122 | { |
|---|
| 123 | assert(_pOrientedGraph); |
|---|
| 124 | // - a node x in X is free iff its in-degree is 0 |
|---|
| 125 | // - a node y in Y is free iff its out-degree is 0 |
|---|
| 126 | return (_bpMap[v] && OrientedInArcIt(*_pOrientedGraph, v) == INVALID) || |
|---|
| 127 | (!_bpMap[v] && OrientedOutArcIt(*_pOrientedGraph, v) == INVALID); |
|---|
| 128 | } |
|---|
| 129 | |
|---|
| 130 | bool isFreeX(const Node& x) |
|---|
| 131 | { |
|---|
| 132 | assert(_bpMap[x] && _pOrientedGraph); |
|---|
| 133 | // - a node x in X is free iff its in-degree is 0 |
|---|
| 134 | return OrientedInArcIt(*_pOrientedGraph, x) == INVALID; |
|---|
| 135 | } |
|---|
| 136 | |
|---|
| 137 | bool isFreeY(const Node& y) |
|---|
| 138 | { |
|---|
| 139 | assert(!_bpMap[y] && _pOrientedGraph); |
|---|
| 140 | // - a node y in Y is free iff its out-degree is 0 |
|---|
| 141 | return OrientedOutArcIt(*_pOrientedGraph, y) == INVALID; |
|---|
| 142 | } |
|---|
| 143 | |
|---|
| 144 | void augmentPath(const Node& y, const PredMap& pred) |
|---|
| 145 | { |
|---|
| 146 | // M' = M ^ EP |
|---|
| 147 | OrientedArc a = pred[y]; |
|---|
| 148 | while (a != INVALID) |
|---|
| 149 | { |
|---|
| 150 | _pOrientedGraph->reverseArc(a); |
|---|
| 151 | |
|---|
| 152 | if ((*_pMatching)[a]) |
|---|
| 153 | { |
|---|
| 154 | _pMatchingMap->set(_pOrientedGraph->source(a), a); |
|---|
| 155 | _pMatchingMap->set(_pOrientedGraph->target(a), a); |
|---|
| 156 | } |
|---|
| 157 | |
|---|
| 158 | // arc has been reversed, so we need the target now |
|---|
| 159 | a = pred[_pOrientedGraph->target(a)]; |
|---|
| 160 | } |
|---|
| 161 | } |
|---|
| 162 | |
|---|
| 163 | void augment(const Node& x, DistMap& dist, PredMap& pred) |
|---|
| 164 | { |
|---|
| 165 | assert(isFreeX(x)); |
|---|
| 166 | |
|---|
| 167 | /** |
|---|
| 168 | * In case maxCardinality == false, we also need to consider |
|---|
| 169 | * augmenting paths starting from x and ending in a matched |
|---|
| 170 | * node x' in X. Augmenting such a path does *not* increase |
|---|
| 171 | * the cardinality of the matching. It may, however, increase |
|---|
| 172 | * the weight of the matching. |
|---|
| 173 | * |
|---|
| 174 | * Along with a shortest path starting from x and ending in |
|---|
| 175 | * a free vertex y in Y, we also determine x' such that |
|---|
| 176 | * y' = pred[x'], |
|---|
| 177 | * (pot[x] + pot[y'] - dist[x, y']) - w(y', x') is maximal |
|---|
| 178 | * |
|---|
| 179 | * Since (y', x') is part of the matching, |
|---|
| 180 | * by primal complementary slackness we have that |
|---|
| 181 | * pot[y'] + pot[x'] = w(y', x'). |
|---|
| 182 | * |
|---|
| 183 | * Hence |
|---|
| 184 | * x' = arg max_{x' \in X} { pot[x] + pot[y'] - dist[x, y']) -w(y', x') } |
|---|
| 185 | * = arg max_{x' \in X} { pot[x] - dist[x, y'] - pot[x'] } |
|---|
| 186 | * = arg max_{x' \in X} { -dist[x, y'] - pot[x'] } |
|---|
| 187 | * = arg min_{x' \in X} { dist[x, y'] + [x'] } |
|---|
| 188 | * |
|---|
| 189 | * We only augment x ->* x' if dist(x,y) > dist[x, y'] + pot[x'] |
|---|
| 190 | * Otherwise we augment x ->* y. |
|---|
| 191 | */ |
|---|
| 192 | |
|---|
| 193 | Value UB = _pot[x]; |
|---|
| 194 | NodeList visitedX, visitedY; |
|---|
| 195 | |
|---|
| 196 | // heap only contains nodes in Y |
|---|
| 197 | HeapCrossRef heapCrossRef(_graph, Heap::PRE_HEAP); |
|---|
| 198 | Heap heap(heapCrossRef); |
|---|
| 199 | |
|---|
| 200 | // add nodes adjacent to x to heap, and update UB |
|---|
| 201 | visitedX.push_back(x); |
|---|
| 202 | for (OrientedOutArcIt a(*_pOrientedGraph, x); a != INVALID; ++a) |
|---|
| 203 | { |
|---|
| 204 | const Node& y = _pOrientedGraph->target(a); |
|---|
| 205 | Value dist_y = dist[x] + _pot[x] + _pot[y] - _weight[a]; |
|---|
| 206 | |
|---|
| 207 | if (dist_y >= UB) |
|---|
| 208 | continue; |
|---|
| 209 | |
|---|
| 210 | if (isFreeY(y)) |
|---|
| 211 | UB = dist_y; |
|---|
| 212 | |
|---|
| 213 | dist[y] = dist_y; |
|---|
| 214 | pred[y] = a; |
|---|
| 215 | |
|---|
| 216 | assert(heap.state(y) == Heap::PRE_HEAP); |
|---|
| 217 | heap.push(y, dist_y); |
|---|
| 218 | } |
|---|
| 219 | |
|---|
| 220 | Node x_min = x; |
|---|
| 221 | Value minDist = 0, x_min_dist = _pot[x]; |
|---|
| 222 | |
|---|
| 223 | while (true) |
|---|
| 224 | { |
|---|
| 225 | assert(heap.empty() || heap.prio() == dist[heap.top()]); |
|---|
| 226 | |
|---|
| 227 | if (heap.empty() || heap.prio() >= x_min_dist) |
|---|
| 228 | { |
|---|
| 229 | minDist = x_min_dist; |
|---|
| 230 | |
|---|
| 231 | if (x_min != x) |
|---|
| 232 | { |
|---|
| 233 | // we have an augmenting path between x and x_min |
|---|
| 234 | // that doesn't increase the matching size |
|---|
| 235 | _matchingWeight += _pot[x] - x_min_dist; |
|---|
| 236 | |
|---|
| 237 | // x_min becomes free, and will always remain free |
|---|
| 238 | (*_pMatchingMap)[x_min] = INVALID; |
|---|
| 239 | augmentPath(x_min, pred); |
|---|
| 240 | } |
|---|
| 241 | break; |
|---|
| 242 | } |
|---|
| 243 | |
|---|
| 244 | const Node y = heap.top(); |
|---|
| 245 | const Value dist_y = heap.prio(); |
|---|
| 246 | heap.pop(); |
|---|
| 247 | |
|---|
| 248 | visitedY.push_back(y); |
|---|
| 249 | if (isFreeY(y)) |
|---|
| 250 | { |
|---|
| 251 | // we have an augmenting path between x and y |
|---|
| 252 | augmentPath(y, pred); |
|---|
| 253 | _matchingSize++; |
|---|
| 254 | |
|---|
| 255 | assert(_pot[y] == 0); |
|---|
| 256 | _matchingWeight += _pot[x] - dist_y; |
|---|
| 257 | |
|---|
| 258 | minDist = dist_y; |
|---|
| 259 | break; |
|---|
| 260 | } |
|---|
| 261 | else |
|---|
| 262 | { |
|---|
| 263 | // y is not free, so there *must* be only one arc pointing toward X |
|---|
| 264 | OrientedArc a = OrientedOutArcIt(*_pOrientedGraph, y); |
|---|
| 265 | assert(a != INVALID && ++OrientedOutArcIt(*_pOrientedGraph, y) == INVALID); |
|---|
| 266 | |
|---|
| 267 | const Node& x2 = _pOrientedGraph->target(a); |
|---|
| 268 | pred[x2] = a; |
|---|
| 269 | visitedX.push_back(x2); |
|---|
| 270 | dist[x2] = dist_y; // matched edges have a reduced weight of 0 |
|---|
| 271 | |
|---|
| 272 | // it must hold that x2 has only one incoming arc |
|---|
| 273 | assert(OrientedInArcIt(*_pOrientedGraph, x2) != INVALID && |
|---|
| 274 | ++OrientedInArcIt(*_pOrientedGraph, x2) == INVALID); |
|---|
| 275 | |
|---|
| 276 | if (dist_y + _pot[x2] < x_min_dist) |
|---|
| 277 | { |
|---|
| 278 | x_min = x2; |
|---|
| 279 | x_min_dist = dist_y + _pot[x2]; |
|---|
| 280 | |
|---|
| 281 | // we have a better criterion now |
|---|
| 282 | if (UB > x_min_dist) |
|---|
| 283 | UB = x_min_dist; |
|---|
| 284 | } |
|---|
| 285 | |
|---|
| 286 | for (OrientedOutArcIt a2(*_pOrientedGraph, x2); a2 != INVALID; ++a2) |
|---|
| 287 | { |
|---|
| 288 | const Node& y2 = _pOrientedGraph->target(a2); |
|---|
| 289 | Value dist_y2 = dist[x2] + _pot[x2] + _pot[y2] - _weight[a2]; |
|---|
| 290 | |
|---|
| 291 | if (dist_y2 >= UB) |
|---|
| 292 | continue; |
|---|
| 293 | |
|---|
| 294 | if (isFreeY(y2)) |
|---|
| 295 | UB = dist_y2; |
|---|
| 296 | |
|---|
| 297 | if (heap.state(y2) == Heap::PRE_HEAP) |
|---|
| 298 | { |
|---|
| 299 | dist[y2] = dist_y2; |
|---|
| 300 | pred[y2] = a2; |
|---|
| 301 | heap.push(y2, dist_y2); |
|---|
| 302 | } |
|---|
| 303 | else if (dist_y2 < dist[y2]) |
|---|
| 304 | { |
|---|
| 305 | dist[y2] = dist_y2; |
|---|
| 306 | pred[y2] = a2; |
|---|
| 307 | heap.decrease(y2, dist_y2); |
|---|
| 308 | } |
|---|
| 309 | } |
|---|
| 310 | } |
|---|
| 311 | } |
|---|
| 312 | |
|---|
| 313 | // restore primal complementary slackness |
|---|
| 314 | // (reduced edge weight of matching edges should be 0) |
|---|
| 315 | for (typename NodeList::const_iterator itX = visitedX.begin(); |
|---|
| 316 | itX != visitedX.end(); itX++) |
|---|
| 317 | { |
|---|
| 318 | const Node& x = *itX; |
|---|
| 319 | assert(minDist - dist[x] >= 0); |
|---|
| 320 | _pot[x] -= minDist - dist[x]; |
|---|
| 321 | assert(_pot[x] >= 0); |
|---|
| 322 | } |
|---|
| 323 | |
|---|
| 324 | for (typename NodeList::const_iterator itY = visitedY.begin(); |
|---|
| 325 | itY != visitedY.end(); itY++) |
|---|
| 326 | { |
|---|
| 327 | const Node& y = *itY; |
|---|
| 328 | assert(minDist - dist[y] >= 0); |
|---|
| 329 | _pot[y] += minDist - dist[y]; |
|---|
| 330 | assert(_pot[y] >= 0); |
|---|
| 331 | } |
|---|
| 332 | } |
|---|
| 333 | |
|---|
| 334 | public: |
|---|
| 335 | /// \brief Constructor |
|---|
| 336 | /// |
|---|
| 337 | /// Constructor. |
|---|
| 338 | /// |
|---|
| 339 | /// \param graph is the input graph |
|---|
| 340 | /// \param weight are the edge weights |
|---|
| 341 | MaxWeightedBipartiteMatching(const Graph& graph, const WeightMap& weight) |
|---|
| 342 | : _graph(graph) |
|---|
| 343 | , _pOrientedGraph(NULL) |
|---|
| 344 | , _weight(weight) |
|---|
| 345 | , _bpMap(graph) |
|---|
| 346 | , _pot(graph, 0) |
|---|
| 347 | , _pMatching(NULL) |
|---|
| 348 | , _matchingWeight(0) |
|---|
| 349 | , _pMatchingMap(NULL) |
|---|
| 350 | , _matchingSize(0) |
|---|
| 351 | , _X() |
|---|
| 352 | , _Y() |
|---|
| 353 | { |
|---|
| 354 | // TODO: a swap might be beneficial if |_X| > |_Y| |
|---|
| 355 | if (bipartitePartitions(_graph, _bpMap)) |
|---|
| 356 | { |
|---|
| 357 | for (NodeIt v(_graph); v != INVALID; ++v) |
|---|
| 358 | { |
|---|
| 359 | if (_bpMap[v]) |
|---|
| 360 | _X.push_back(v); |
|---|
| 361 | else |
|---|
| 362 | _Y.push_back(v); |
|---|
| 363 | } |
|---|
| 364 | } |
|---|
| 365 | } |
|---|
| 366 | |
|---|
| 367 | /// \brief Constructor |
|---|
| 368 | /// |
|---|
| 369 | /// Constructor. Instead of computing a bipartite partition, X and Y |
|---|
| 370 | /// are used. |
|---|
| 371 | /// |
|---|
| 372 | /// \param graph is the input graph |
|---|
| 373 | /// \param X is the first color class of the given bipartite graph |
|---|
| 374 | /// \param Y is the second color class of the given bipartite graph |
|---|
| 375 | /// \param weight are the edge weights |
|---|
| 376 | /// |
|---|
| 377 | /// \pre All edges need to be oriented from Y to X. |
|---|
| 378 | MaxWeightedBipartiteMatching(const Graph& graph, |
|---|
| 379 | const NodeList& X, const NodeList& Y, |
|---|
| 380 | const WeightMap& weight) |
|---|
| 381 | : _graph(graph) |
|---|
| 382 | , _pOrientedGraph(NULL) |
|---|
| 383 | , _weight(weight) |
|---|
| 384 | , _bpMap(graph, false) |
|---|
| 385 | , _pot(graph, 0) |
|---|
| 386 | , _pMatching(NULL) |
|---|
| 387 | , _matchingWeight(0) |
|---|
| 388 | , _pMatchingMap(NULL) |
|---|
| 389 | , _matchingSize(0) |
|---|
| 390 | , _X(X)//(X.size() <= Y.size() ? X : Y) |
|---|
| 391 | , _Y(Y)//(X.size() > Y.size() ? X : Y) |
|---|
| 392 | { |
|---|
| 393 | // set _bpMap |
|---|
| 394 | for (typename NodeList::const_iterator itX = _X.begin(); |
|---|
| 395 | itX != _X.end(); itX++) |
|---|
| 396 | { |
|---|
| 397 | _bpMap[*itX] = true; |
|---|
| 398 | } |
|---|
| 399 | } |
|---|
| 400 | |
|---|
| 401 | ~MaxWeightedBipartiteMatching() |
|---|
| 402 | { |
|---|
| 403 | delete _pMatching; |
|---|
| 404 | delete _pOrientedGraph; |
|---|
| 405 | delete _pMatchingMap; |
|---|
| 406 | } |
|---|
| 407 | |
|---|
| 408 | /// \brief Initialize the algorithm |
|---|
| 409 | /// |
|---|
| 410 | /// This function initializes the algorithm. |
|---|
| 411 | /// |
|---|
| 412 | /// \param greedy indicates whether a nonempty initial matching |
|---|
| 413 | /// should be used; this might be faster in some cases. |
|---|
| 414 | void init(bool greedy = false) |
|---|
| 415 | { |
|---|
| 416 | // reset data structures |
|---|
| 417 | delete _pMatching; |
|---|
| 418 | delete _pOrientedGraph; |
|---|
| 419 | delete _pMatchingMap; |
|---|
| 420 | |
|---|
| 421 | _pMatchingMap = new MatchingMap(_graph, INVALID); |
|---|
| 422 | _pMatching = new EdgeBoolMap(_graph, false); |
|---|
| 423 | _pOrientedGraph = new OrientedGraph(_graph, *_pMatching); |
|---|
| 424 | _matchingWeight = 0; |
|---|
| 425 | _matchingSize = 0; |
|---|
| 426 | |
|---|
| 427 | // _pot[x] is set to maximum incident edge weight |
|---|
| 428 | for (typename NodeList::const_iterator itX = _X.begin(); |
|---|
| 429 | itX != _X.end(); itX++) |
|---|
| 430 | { |
|---|
| 431 | const Node& x = *itX; |
|---|
| 432 | |
|---|
| 433 | Value maxWeight = 0; |
|---|
| 434 | Edge e_max = INVALID; |
|---|
| 435 | for (IncEdgeIt e(_graph, *itX); e != INVALID; ++e) |
|---|
| 436 | { |
|---|
| 437 | // _pot[y] = 0 for all y \in Y |
|---|
| 438 | _pot[_graph.oppositeNode(x, e)] = 0; |
|---|
| 439 | |
|---|
| 440 | if (_weight[e] > maxWeight) |
|---|
| 441 | { |
|---|
| 442 | maxWeight = _weight[e]; |
|---|
| 443 | e_max = e; |
|---|
| 444 | } |
|---|
| 445 | } |
|---|
| 446 | |
|---|
| 447 | if (e_max != INVALID) |
|---|
| 448 | { |
|---|
| 449 | _pot.set(x, maxWeight); |
|---|
| 450 | const Node& y = _graph.oppositeNode(x, e_max); |
|---|
| 451 | if (greedy && isFreeY(y)) |
|---|
| 452 | { |
|---|
| 453 | _matchingWeight += maxWeight; |
|---|
| 454 | _matchingSize++; |
|---|
| 455 | _pMatching->set(e_max, true); |
|---|
| 456 | _pMatchingMap->set(x, e_max); |
|---|
| 457 | _pMatchingMap->set(y, e_max); |
|---|
| 458 | } |
|---|
| 459 | } |
|---|
| 460 | } |
|---|
| 461 | } |
|---|
| 462 | |
|---|
| 463 | /// \brief Start the algorithm |
|---|
| 464 | /// |
|---|
| 465 | /// This function starts the algorithm. |
|---|
| 466 | /// |
|---|
| 467 | /// \pre \ref init() must have been called before using this function. |
|---|
| 468 | void start() |
|---|
| 469 | { |
|---|
| 470 | DistMap dist(_graph, 0); |
|---|
| 471 | PredMap pred(_graph, INVALID); |
|---|
| 472 | |
|---|
| 473 | for (typename NodeList::const_iterator itX = _X.begin(); |
|---|
| 474 | itX != _X.end(); itX++) |
|---|
| 475 | { |
|---|
| 476 | const Node& x = *itX; |
|---|
| 477 | |
|---|
| 478 | if (isFreeX(x)) |
|---|
| 479 | augment(x, dist, pred); |
|---|
| 480 | } |
|---|
| 481 | } |
|---|
| 482 | |
|---|
| 483 | /// \brief Run the algorithm. |
|---|
| 484 | /// |
|---|
| 485 | /// This method runs the \c %MaxWeightedBipartiteMatching algorithm. |
|---|
| 486 | /// |
|---|
| 487 | /// \param greedy indicates whether a nonempty initial matching |
|---|
| 488 | /// should be used; this might be faster in some cases. |
|---|
| 489 | /// |
|---|
| 490 | /// \note mwbm.run() is just a shortcut of the following code. |
|---|
| 491 | /// \code |
|---|
| 492 | /// mwbm.init(); |
|---|
| 493 | /// mwbm.start(); |
|---|
| 494 | /// \endcode |
|---|
| 495 | void run(bool greedy = false) |
|---|
| 496 | { |
|---|
| 497 | init(); |
|---|
| 498 | start(greedy); |
|---|
| 499 | } |
|---|
| 500 | |
|---|
| 501 | /// \brief Checks whether the solution is optimal |
|---|
| 502 | /// |
|---|
| 503 | /// Checks using the dual solution whether the primal solution is optimal. |
|---|
| 504 | /// |
|---|
| 505 | /// \return \c true if the solution is optimal. |
|---|
| 506 | bool checkOptimality() const |
|---|
| 507 | { |
|---|
| 508 | assert(_pMatchingMap); |
|---|
| 509 | |
|---|
| 510 | /* |
|---|
| 511 | * Primal: |
|---|
| 512 | * max \sum_{i,j} c_{ij} x_{ij} |
|---|
| 513 | * s.t. \sum_i x_{ij} <= 1 |
|---|
| 514 | * \sum_j x_{ij} <= 1 |
|---|
| 515 | * x_{ij} >= 0 |
|---|
| 516 | * |
|---|
| 517 | * Dual: |
|---|
| 518 | * min \sum_j p_j + \sum_i r_i |
|---|
| 519 | * s.t. p_j + r_i >= c_{ij} |
|---|
| 520 | * p_j >= 0 |
|---|
| 521 | * r_i >= 0 |
|---|
| 522 | * |
|---|
| 523 | * Solution is optimal iff: |
|---|
| 524 | * - Primal complementary slackness: |
|---|
| 525 | * - x_{ij} = 1 => p_j + r_i = c_{ij} |
|---|
| 526 | * - Dual complementary slackness: |
|---|
| 527 | * - p_j != 0 => \sum_i x_{ij} = 1 |
|---|
| 528 | * - r_i != 0 => \sum_j x_{ij} = 1 |
|---|
| 529 | */ |
|---|
| 530 | |
|---|
| 531 | // check whether primal solution is feasible |
|---|
| 532 | for (NodeIt n(_graph); n != INVALID; ++n) |
|---|
| 533 | { |
|---|
| 534 | int count = 0; |
|---|
| 535 | for (IncEdgeIt e(_graph, n); e != INVALID; ++e) |
|---|
| 536 | { |
|---|
| 537 | if ((*_pMatching)[e]) |
|---|
| 538 | count++; |
|---|
| 539 | } |
|---|
| 540 | |
|---|
| 541 | if (count > 1) |
|---|
| 542 | return false; |
|---|
| 543 | } |
|---|
| 544 | |
|---|
| 545 | // check whether dual solution is feasible |
|---|
| 546 | for (NodeIt n(_graph); n != INVALID; ++n) |
|---|
| 547 | { |
|---|
| 548 | if (_pot[n] < 0) return false; |
|---|
| 549 | } |
|---|
| 550 | for (EdgeIt e(_graph); e != INVALID; ++e) |
|---|
| 551 | { |
|---|
| 552 | if (_pot[_graph.u(e)] + _pot[_graph.v(e)] < _weight[e]) |
|---|
| 553 | return false; |
|---|
| 554 | } |
|---|
| 555 | |
|---|
| 556 | // check primal complementary slackness |
|---|
| 557 | for (EdgeIt e(_graph); e != INVALID; ++e) |
|---|
| 558 | { |
|---|
| 559 | // x_{ij} = 1 => p_j + r_i = c_{ij} |
|---|
| 560 | if ((*_pMatching)[e] && |
|---|
| 561 | _pot[_graph.u(e)] + _pot[_graph.v(e)] != _weight[e]) |
|---|
| 562 | return false; |
|---|
| 563 | } |
|---|
| 564 | |
|---|
| 565 | // check dual complementary slackness |
|---|
| 566 | for (NodeIt n(_graph); n != INVALID; ++n) |
|---|
| 567 | { |
|---|
| 568 | // p_j != 0 => \sum_i x_{ij} = 1 |
|---|
| 569 | // r_i != 0 => \sum_j x_{ij} = 1 |
|---|
| 570 | if (_pot[n] != 0) |
|---|
| 571 | { |
|---|
| 572 | int count = 0; |
|---|
| 573 | for (IncEdgeIt e(_graph, n); e != INVALID; ++e) |
|---|
| 574 | { |
|---|
| 575 | if ((*_pMatching)[e]) |
|---|
| 576 | count++; |
|---|
| 577 | } |
|---|
| 578 | if (count != 1) |
|---|
| 579 | return false; |
|---|
| 580 | } |
|---|
| 581 | } |
|---|
| 582 | |
|---|
| 583 | return true; |
|---|
| 584 | } |
|---|
| 585 | |
|---|
| 586 | /// \brief Return a const reference to the matching map. |
|---|
| 587 | /// |
|---|
| 588 | /// This function returns a const reference to a node map that stores |
|---|
| 589 | /// the matching edge incident to each node. |
|---|
| 590 | /// |
|---|
| 591 | /// \pre init() must have been called before using this function. |
|---|
| 592 | const MatchingMap& matchingMap() const |
|---|
| 593 | { |
|---|
| 594 | assert(_pMatchingMap); |
|---|
| 595 | return *_pMatchingMap; |
|---|
| 596 | } |
|---|
| 597 | |
|---|
| 598 | /// \brief Return the weight of the matching. |
|---|
| 599 | /// |
|---|
| 600 | /// This function returns the weight of the found matching. |
|---|
| 601 | /// |
|---|
| 602 | /// \pre init() must have been called before using this function. |
|---|
| 603 | Value matchingWeight() const |
|---|
| 604 | { |
|---|
| 605 | return _matchingWeight; |
|---|
| 606 | } |
|---|
| 607 | |
|---|
| 608 | /// \brief Return the number of edges in the matching. |
|---|
| 609 | /// |
|---|
| 610 | /// This function returns the number of edges in the matching. |
|---|
| 611 | int matchingSize() const |
|---|
| 612 | { |
|---|
| 613 | return _matchingSize; |
|---|
| 614 | } |
|---|
| 615 | |
|---|
| 616 | /// \brief Return \c true if the given edge is in the matching. |
|---|
| 617 | /// |
|---|
| 618 | /// This function returns \c true if the given edge is in the found |
|---|
| 619 | /// matching. |
|---|
| 620 | /// |
|---|
| 621 | /// \pre init() must have been been called before using this function. |
|---|
| 622 | bool matching(const Edge& e) const |
|---|
| 623 | { |
|---|
| 624 | assert(_pMatching); |
|---|
| 625 | return _pMatching[e] != INVALID; |
|---|
| 626 | } |
|---|
| 627 | |
|---|
| 628 | /// \brief Return the matching edge incident to the given node. |
|---|
| 629 | /// |
|---|
| 630 | /// This function returns the matching edge incident to the |
|---|
| 631 | /// given node in the found matching or \c INVALID if the node is |
|---|
| 632 | /// not covered by the matching. |
|---|
| 633 | /// |
|---|
| 634 | /// \pre init() must have been been called before using this function. |
|---|
| 635 | Edge matching(const Node& n) const |
|---|
| 636 | { |
|---|
| 637 | assert(_pMatchingMap); |
|---|
| 638 | return (*_pMatchingMap)[n]; |
|---|
| 639 | } |
|---|
| 640 | |
|---|
| 641 | /// \brief Return the mate of the given node. |
|---|
| 642 | /// |
|---|
| 643 | /// This function returns the mate of the given node in the found |
|---|
| 644 | /// matching or \c INVALID if the node is not covered by the matching. |
|---|
| 645 | /// |
|---|
| 646 | /// \pre init() must have been been called before using this function. |
|---|
| 647 | Node mate(const Node& n) const |
|---|
| 648 | { |
|---|
| 649 | assert(_pMatchingMap); |
|---|
| 650 | |
|---|
| 651 | if ((*_pMatchingMap)[n] == INVALID) |
|---|
| 652 | return INVALID; |
|---|
| 653 | else |
|---|
| 654 | return _graph.oppositeNode(n, (*_pMatchingMap)[n]); |
|---|
| 655 | } |
|---|
| 656 | }; |
|---|
| 657 | |
|---|
| 658 | /// \ingroup matching |
|---|
| 659 | /// |
|---|
| 660 | /// \brief Maximum weight matching in (dense) bipartite graphs |
|---|
| 661 | /// |
|---|
| 662 | /// This class provides an implementation of the classical Hungarian |
|---|
| 663 | /// algorithm for finding a maximum weight matching in an undirected |
|---|
| 664 | /// bipartite graph. This algorithm follows the primal-dual schema. |
|---|
| 665 | /// The time complexity is \f$O(n^3)\f$. In case the bipartite graph is |
|---|
| 666 | /// sparse, it is better to use \ref MaxWeightedBipartiteMatching, which |
|---|
| 667 | /// has a time complexity of \f$O(n^2 \log n)\f$ for sparse graphs. |
|---|
| 668 | #ifdef DOXYGEN |
|---|
| 669 | template <typename GR, typename WM> |
|---|
| 670 | #else |
|---|
| 671 | template <typename GR, |
|---|
| 672 | typename WM = typename GR::template EdgeMap<int> > |
|---|
| 673 | #endif |
|---|
| 674 | class MaxWeightedDenseBipartiteMatching |
|---|
| 675 | { |
|---|
| 676 | public: |
|---|
| 677 | /// The graph type of the algorithm |
|---|
| 678 | typedef GR Graph; |
|---|
| 679 | /// The type of the edge weight map |
|---|
| 680 | typedef WM WeightMap; |
|---|
| 681 | /// The value type of the edge weights |
|---|
| 682 | typedef typename WeightMap::Value Value; |
|---|
| 683 | /// The type of the matching map |
|---|
| 684 | typedef typename Graph::template NodeMap<typename Graph::Edge> MatchingMap; |
|---|
| 685 | /// The type of a list of nodes |
|---|
| 686 | typedef std::list<typename Graph::Node> NodeList; |
|---|
| 687 | |
|---|
| 688 | private: |
|---|
| 689 | TEMPLATE_GRAPH_TYPEDEFS(Graph); |
|---|
| 690 | |
|---|
| 691 | typedef typename Graph::template NodeMap<int> IdMap; |
|---|
| 692 | typedef typename Graph::template NodeMap<bool> BoolMap; |
|---|
| 693 | |
|---|
| 694 | typedef std::vector<Node> ReverseIdVector; |
|---|
| 695 | typedef std::vector<int> MateVector; |
|---|
| 696 | typedef std::vector<Value> WeightVector; |
|---|
| 697 | typedef std::vector<bool> BoolVector; |
|---|
| 698 | |
|---|
| 699 | class BpEdgeT |
|---|
| 700 | { |
|---|
| 701 | private: |
|---|
| 702 | Value _weight; |
|---|
| 703 | Edge _edge; |
|---|
| 704 | |
|---|
| 705 | public: |
|---|
| 706 | BpEdgeT() |
|---|
| 707 | : _weight(0) |
|---|
| 708 | , _edge(INVALID) |
|---|
| 709 | { |
|---|
| 710 | } |
|---|
| 711 | |
|---|
| 712 | void setWeight(Value weight) |
|---|
| 713 | { |
|---|
| 714 | _weight = weight; |
|---|
| 715 | } |
|---|
| 716 | |
|---|
| 717 | Value getWeight() const |
|---|
| 718 | { |
|---|
| 719 | return _weight; |
|---|
| 720 | } |
|---|
| 721 | |
|---|
| 722 | void setEdge(const Edge& edge) |
|---|
| 723 | { |
|---|
| 724 | _edge = edge; |
|---|
| 725 | } |
|---|
| 726 | |
|---|
| 727 | const Edge& getEdge() const |
|---|
| 728 | { |
|---|
| 729 | return _edge; |
|---|
| 730 | } |
|---|
| 731 | }; |
|---|
| 732 | |
|---|
| 733 | typedef std::vector<std::vector<BpEdgeT> > AdjacencyMatrixType; |
|---|
| 734 | |
|---|
| 735 | const Graph& _graph; |
|---|
| 736 | |
|---|
| 737 | const WeightMap& _weight; |
|---|
| 738 | IdMap _idMap; |
|---|
| 739 | MatchingMap _matchingMap; |
|---|
| 740 | |
|---|
| 741 | AdjacencyMatrixType _adjacencyMatrix; |
|---|
| 742 | |
|---|
| 743 | ReverseIdVector _reverseIdMapX; |
|---|
| 744 | ReverseIdVector _reverseIdMapY; |
|---|
| 745 | |
|---|
| 746 | WeightVector _labelMapX; |
|---|
| 747 | WeightVector _labelMapY; |
|---|
| 748 | |
|---|
| 749 | MateVector _mateMapX; |
|---|
| 750 | MateVector _mateMapY; |
|---|
| 751 | |
|---|
| 752 | int _nX; |
|---|
| 753 | int _nY; |
|---|
| 754 | |
|---|
| 755 | int _matchingSize; |
|---|
| 756 | Value _matchingWeight; |
|---|
| 757 | |
|---|
| 758 | static const Value _minValue; |
|---|
| 759 | static const Value _maxValue; |
|---|
| 760 | |
|---|
| 761 | void initAdjacencyMatrix(const NodeList& nodesX, const NodeList& nodesY) |
|---|
| 762 | { |
|---|
| 763 | // adjacency matrix has dimensions |X| * |Y|, |
|---|
| 764 | // every entry in this matrix is initialized to (0, INVALID) |
|---|
| 765 | _adjacencyMatrix = AdjacencyMatrixType(nodesX.size(), |
|---|
| 766 | std::vector<BpEdgeT>(nodesY.size(), BpEdgeT())); |
|---|
| 767 | |
|---|
| 768 | // fill in edge weights by iterating through incident edges of nodes in X |
|---|
| 769 | for (typename NodeList::const_iterator itX = nodesX.begin(); |
|---|
| 770 | itX != nodesX.end(); itX++) |
|---|
| 771 | { |
|---|
| 772 | Node x = *itX; |
|---|
| 773 | int idX = _idMap[x]; |
|---|
| 774 | |
|---|
| 775 | for (IncEdgeIt e(_graph, x); e != INVALID; ++e) |
|---|
| 776 | { |
|---|
| 777 | Node y = _graph.oppositeNode(x, e); |
|---|
| 778 | int idY = _idMap[y]; |
|---|
| 779 | |
|---|
| 780 | Value w = _weight[e]; |
|---|
| 781 | |
|---|
| 782 | BpEdgeT& item = _adjacencyMatrix[idX][idY]; |
|---|
| 783 | item.setEdge(e); |
|---|
| 784 | item.setWeight(w); |
|---|
| 785 | |
|---|
| 786 | // label of a node x in X is initialized to maximum weight |
|---|
| 787 | // of edges incident to x |
|---|
| 788 | if (w > _labelMapX[idX]) |
|---|
| 789 | _labelMapX[idX] = w; |
|---|
| 790 | } // for e |
|---|
| 791 | } // for itX |
|---|
| 792 | } |
|---|
| 793 | |
|---|
| 794 | void updateSlacks(WeightVector& slack, int x) |
|---|
| 795 | { |
|---|
| 796 | Value lx = _labelMapX[x]; |
|---|
| 797 | for (int y = 0; y < _nY; y++) |
|---|
| 798 | { |
|---|
| 799 | // slack[y] = min_{x \in S} [l(x) + l(y) - w(x, y)] |
|---|
| 800 | Value val = lx + _labelMapY[y] - _adjacencyMatrix[x][y].getWeight(); |
|---|
| 801 | if (slack[y] > val) |
|---|
| 802 | slack[y] = val; |
|---|
| 803 | } |
|---|
| 804 | } |
|---|
| 805 | |
|---|
| 806 | void updateLabels(const BoolVector& setS, const BoolVector& setT, WeightVector& slack) |
|---|
| 807 | { |
|---|
| 808 | // recall that slack[y] = min_{x \in S} [l(x) + l(y) - w(x,y)] |
|---|
| 809 | |
|---|
| 810 | // delta = min_{y \not \in T} (slack[y]) |
|---|
| 811 | Value delta = _maxValue; |
|---|
| 812 | for (int y = 0; y < _nY; y++) |
|---|
| 813 | { |
|---|
| 814 | if (!setT[y] && slack[y] < delta) |
|---|
| 815 | delta = slack[y]; |
|---|
| 816 | } |
|---|
| 817 | |
|---|
| 818 | // update labels in X |
|---|
| 819 | for (int x = 0; x < _nX; x++) |
|---|
| 820 | { |
|---|
| 821 | if (setS[x]) |
|---|
| 822 | _labelMapX[x] -= delta; |
|---|
| 823 | } |
|---|
| 824 | |
|---|
| 825 | // update labels in Y |
|---|
| 826 | for (int y = 0; y < _nY; y++) |
|---|
| 827 | { |
|---|
| 828 | if (setT[y]) |
|---|
| 829 | _labelMapY[y] += delta; |
|---|
| 830 | else |
|---|
| 831 | { |
|---|
| 832 | // update slacks |
|---|
| 833 | // remember that l(x) + l(y) hasn't changed for x \in S and y \in T |
|---|
| 834 | // the only thing that has changed is l(x) + l(y) for x \in S and y \not \in T |
|---|
| 835 | slack[y] -= delta; |
|---|
| 836 | } |
|---|
| 837 | } |
|---|
| 838 | } |
|---|
| 839 | |
|---|
| 840 | void buildMatchingMap() |
|---|
| 841 | { |
|---|
| 842 | _matchingWeight = 0; |
|---|
| 843 | _matchingSize = 0; |
|---|
| 844 | |
|---|
| 845 | for (int x = 0; x < _nX; x++) |
|---|
| 846 | { |
|---|
| 847 | assert(_mateMapX[x] != -1); |
|---|
| 848 | |
|---|
| 849 | int y = _mateMapX[x]; |
|---|
| 850 | |
|---|
| 851 | const Edge& e = _adjacencyMatrix[x][y].getEdge(); |
|---|
| 852 | _matchingMap[_reverseIdMapX[x]] = _matchingMap[_reverseIdMapY[y]] = e; |
|---|
| 853 | |
|---|
| 854 | if (e != INVALID) |
|---|
| 855 | { |
|---|
| 856 | // only edges that where present in the original graph count as a matching |
|---|
| 857 | _matchingSize++; |
|---|
| 858 | _matchingWeight += _weight[e]; |
|---|
| 859 | } |
|---|
| 860 | } |
|---|
| 861 | } |
|---|
| 862 | |
|---|
| 863 | public: |
|---|
| 864 | /// \brief Constructor |
|---|
| 865 | /// |
|---|
| 866 | /// Constructor. |
|---|
| 867 | /// |
|---|
| 868 | /// \param graph is the input graph |
|---|
| 869 | /// \param weight are the edge weights |
|---|
| 870 | MaxWeightedDenseBipartiteMatching(const Graph& graph, |
|---|
| 871 | const WeightMap& weight) |
|---|
| 872 | : _graph(graph) |
|---|
| 873 | , _weight(weight) |
|---|
| 874 | , _idMap(graph, -1) |
|---|
| 875 | , _matchingMap(graph, INVALID) |
|---|
| 876 | , _adjacencyMatrix() |
|---|
| 877 | , _reverseIdMapX() |
|---|
| 878 | , _reverseIdMapY() |
|---|
| 879 | , _labelMapX() |
|---|
| 880 | , _labelMapY() |
|---|
| 881 | , _mateMapX() |
|---|
| 882 | , _mateMapY() |
|---|
| 883 | , _nX(0) |
|---|
| 884 | , _nY(0) |
|---|
| 885 | , _matchingSize(0) |
|---|
| 886 | , _matchingWeight(0) |
|---|
| 887 | { |
|---|
| 888 | } |
|---|
| 889 | |
|---|
| 890 | /// \brief Initialize the algorithm |
|---|
| 891 | /// |
|---|
| 892 | /// This function initializes the algorithm. |
|---|
| 893 | /// |
|---|
| 894 | /// \return \c false if the provided graph is not bipartite. |
|---|
| 895 | bool init() |
|---|
| 896 | { |
|---|
| 897 | // Compute bipartite partitions |
|---|
| 898 | BoolMap bpMap(_graph); |
|---|
| 899 | |
|---|
| 900 | if (!bipartitePartitions(_graph, bpMap)) |
|---|
| 901 | return false; |
|---|
| 902 | |
|---|
| 903 | NodeList nodesX, nodesY; |
|---|
| 904 | for (NodeIt v(_graph); v != INVALID; ++v) |
|---|
| 905 | { |
|---|
| 906 | if (bpMap[v]) |
|---|
| 907 | nodesX.push_back(v); |
|---|
| 908 | else |
|---|
| 909 | nodesY.push_back(v); |
|---|
| 910 | } |
|---|
| 911 | |
|---|
| 912 | init(nodesX, nodesY); |
|---|
| 913 | return true; |
|---|
| 914 | } |
|---|
| 915 | |
|---|
| 916 | |
|---|
| 917 | /// \brief Initialize the algorithm |
|---|
| 918 | /// |
|---|
| 919 | /// This function initializes the algorithm. |
|---|
| 920 | /// |
|---|
| 921 | /// \param nodesX is the first color class of the given bipartite graph |
|---|
| 922 | /// \param nodesY is the second color class of the given bipartite graph |
|---|
| 923 | void init(const NodeList& nodesX, const NodeList& nodesY) |
|---|
| 924 | { |
|---|
| 925 | // we need that |X| < |Y| |
|---|
| 926 | const NodeList& X = nodesX.size() < nodesY.size() ? nodesX : nodesY; |
|---|
| 927 | const NodeList& Y = nodesX.size() < nodesY.size() ? nodesY : nodesX; |
|---|
| 928 | |
|---|
| 929 | _nX = static_cast<int>(X.size()); |
|---|
| 930 | _nY = static_cast<int>(Y.size()); |
|---|
| 931 | |
|---|
| 932 | // init matching is empty |
|---|
| 933 | _mateMapX = MateVector(_nX, -1); |
|---|
| 934 | _mateMapY = MateVector(_nY, -1); |
|---|
| 935 | |
|---|
| 936 | _reverseIdMapX = ReverseIdVector(_nX); |
|---|
| 937 | _reverseIdMapY = ReverseIdVector(_nY); |
|---|
| 938 | |
|---|
| 939 | // labels of nodes in X are initialized to -INF, |
|---|
| 940 | // these will be updated during initAdjacencyMatrix() |
|---|
| 941 | _labelMapX = WeightVector(_nX, _minValue); |
|---|
| 942 | |
|---|
| 943 | // labels of nodes in Y are initialized to 0, |
|---|
| 944 | // these won't be updated during initAdjacencyMatrix() |
|---|
| 945 | _labelMapY = WeightVector(_nY, 0); |
|---|
| 946 | |
|---|
| 947 | int x = 0; |
|---|
| 948 | for (typename NodeList::const_iterator itX = X.begin(); |
|---|
| 949 | itX != X.end(); itX++, x++) |
|---|
| 950 | { |
|---|
| 951 | _idMap[*itX] = x; |
|---|
| 952 | _reverseIdMapX[x] = *itX; |
|---|
| 953 | } |
|---|
| 954 | |
|---|
| 955 | int y = 0; |
|---|
| 956 | for (typename NodeList::const_iterator itY = Y.begin(); |
|---|
| 957 | itY != Y.end(); itY++, y++) |
|---|
| 958 | { |
|---|
| 959 | _idMap[*itY] = y; |
|---|
| 960 | _reverseIdMapY[y] = *itY; |
|---|
| 961 | } |
|---|
| 962 | |
|---|
| 963 | initAdjacencyMatrix(X, Y); |
|---|
| 964 | } |
|---|
| 965 | |
|---|
| 966 | /// \brief Run the algorithm. |
|---|
| 967 | /// |
|---|
| 968 | /// This method runs the \c %MaxWeightedDenseBipartiteMatching algorithm. |
|---|
| 969 | /// |
|---|
| 970 | /// \note mwdbm.run() is just a shortcut of the following code. |
|---|
| 971 | /// \code |
|---|
| 972 | /// if (mwdbm.init() |
|---|
| 973 | /// mwdbm.start(); |
|---|
| 974 | /// \endcode |
|---|
| 975 | void run() |
|---|
| 976 | { |
|---|
| 977 | if (init()); |
|---|
| 978 | start(); |
|---|
| 979 | } |
|---|
| 980 | |
|---|
| 981 | /// \brief Start the algorithm |
|---|
| 982 | /// |
|---|
| 983 | /// This function starts the algorithm. |
|---|
| 984 | /// |
|---|
| 985 | /// \pre \ref init() must have been called before using this function. |
|---|
| 986 | void start() |
|---|
| 987 | { |
|---|
| 988 | // maps y in Y to x in X by which it was discovered |
|---|
| 989 | MateVector discoveredY(_nY, -1); |
|---|
| 990 | |
|---|
| 991 | int matchingSize = 0; |
|---|
| 992 | |
|---|
| 993 | // pick a root |
|---|
| 994 | for (int r = 0; r < _nX; ) |
|---|
| 995 | { |
|---|
| 996 | assert(_mateMapX[r] == -1); |
|---|
| 997 | |
|---|
| 998 | // clear slack map, i.e. set all slacks to +INF |
|---|
| 999 | WeightVector slack(_nY, _maxValue); |
|---|
| 1000 | |
|---|
| 1001 | // initially T = {} |
|---|
| 1002 | BoolVector setT(_nY, false); |
|---|
| 1003 | |
|---|
| 1004 | // initially S = {r} |
|---|
| 1005 | BoolVector setS(_nX, false); |
|---|
| 1006 | setS[r] = true; |
|---|
| 1007 | |
|---|
| 1008 | std::queue<int> queue; |
|---|
| 1009 | queue.push(r); |
|---|
| 1010 | |
|---|
| 1011 | updateSlacks(slack, r); |
|---|
| 1012 | |
|---|
| 1013 | bool augmented = false; |
|---|
| 1014 | while (!queue.empty() && !augmented) |
|---|
| 1015 | { |
|---|
| 1016 | int x = queue.front(); |
|---|
| 1017 | queue.pop(); |
|---|
| 1018 | |
|---|
| 1019 | for (int y = 0; y < _nY; y++) |
|---|
| 1020 | { |
|---|
| 1021 | if (!setT[y] && |
|---|
| 1022 | _labelMapX[x] + _labelMapY[y] == _adjacencyMatrix[x][y].getWeight()) |
|---|
| 1023 | { |
|---|
| 1024 | // y was (first) discovered by x |
|---|
| 1025 | discoveredY[y] = x; |
|---|
| 1026 | |
|---|
| 1027 | if (_mateMapY[y] != -1) // y is matched, extend alternating tree |
|---|
| 1028 | { |
|---|
| 1029 | int z = _mateMapY[y]; |
|---|
| 1030 | |
|---|
| 1031 | // add z to queue if not in S |
|---|
| 1032 | if (!setS[z]) |
|---|
| 1033 | { |
|---|
| 1034 | setS[z] = true; |
|---|
| 1035 | queue.push(z); |
|---|
| 1036 | updateSlacks(slack, z); |
|---|
| 1037 | } |
|---|
| 1038 | |
|---|
| 1039 | setT[y] = true; |
|---|
| 1040 | } |
|---|
| 1041 | else // y is free, we have an augmenting path between r and y |
|---|
| 1042 | { |
|---|
| 1043 | matchingSize++; |
|---|
| 1044 | |
|---|
| 1045 | int cx, ty, cy = y; |
|---|
| 1046 | do { |
|---|
| 1047 | cx = discoveredY[cy]; |
|---|
| 1048 | ty = _mateMapX[cx]; |
|---|
| 1049 | |
|---|
| 1050 | _mateMapX[cx] = cy; |
|---|
| 1051 | _mateMapY[cy] = cx; |
|---|
| 1052 | |
|---|
| 1053 | cy = ty; |
|---|
| 1054 | } while (cx != r); |
|---|
| 1055 | |
|---|
| 1056 | // we found an augmenting path, start a new iteration of the first for loop |
|---|
| 1057 | augmented = true; |
|---|
| 1058 | break; // break for y |
|---|
| 1059 | } |
|---|
| 1060 | } |
|---|
| 1061 | } // y \not in T such that (r,y) in E_l |
|---|
| 1062 | } // queue |
|---|
| 1063 | |
|---|
| 1064 | if (!augmented) |
|---|
| 1065 | updateLabels(setS, setT, slack); |
|---|
| 1066 | else |
|---|
| 1067 | r++; |
|---|
| 1068 | } |
|---|
| 1069 | |
|---|
| 1070 | buildMatchingMap(); |
|---|
| 1071 | } |
|---|
| 1072 | |
|---|
| 1073 | /// \brief Return the weight of the matching. |
|---|
| 1074 | /// |
|---|
| 1075 | /// This function returns the weight of the found matching. |
|---|
| 1076 | /// |
|---|
| 1077 | /// \pre init() must have been called before using this function. |
|---|
| 1078 | Value matchingWeight() const |
|---|
| 1079 | { |
|---|
| 1080 | return _matchingWeight; |
|---|
| 1081 | } |
|---|
| 1082 | |
|---|
| 1083 | /// \brief Return the number of edges in the matching. |
|---|
| 1084 | /// |
|---|
| 1085 | /// This function returns the number of edges in the matching. |
|---|
| 1086 | int matchingSize() const |
|---|
| 1087 | { |
|---|
| 1088 | return _matchingSize; |
|---|
| 1089 | } |
|---|
| 1090 | |
|---|
| 1091 | /// \brief Return \c true if the given edge is in the matching. |
|---|
| 1092 | /// |
|---|
| 1093 | /// This function returns \c true if the given edge is in the found |
|---|
| 1094 | /// matching. |
|---|
| 1095 | /// |
|---|
| 1096 | /// \pre init() must have been been called before using this function. |
|---|
| 1097 | bool matching(const Edge& e) const |
|---|
| 1098 | { |
|---|
| 1099 | return _matchingMap[_graph.u(e)] != INVALID; |
|---|
| 1100 | } |
|---|
| 1101 | |
|---|
| 1102 | /// \brief Return the matching edge incident to the given node. |
|---|
| 1103 | /// |
|---|
| 1104 | /// This function returns the matching edge incident to the |
|---|
| 1105 | /// given node in the found matching or \c INVALID if the node is |
|---|
| 1106 | /// not covered by the matching. |
|---|
| 1107 | /// |
|---|
| 1108 | /// \pre init() must have been been called before using this function. |
|---|
| 1109 | Edge matching(const Node& n) const |
|---|
| 1110 | { |
|---|
| 1111 | return _matchingMap[n]; |
|---|
| 1112 | } |
|---|
| 1113 | |
|---|
| 1114 | /// \brief Return the mate of the given node. |
|---|
| 1115 | /// |
|---|
| 1116 | /// This function returns the mate of the given node in the found |
|---|
| 1117 | /// matching or \c INVALID if the node is not covered by the matching. |
|---|
| 1118 | /// |
|---|
| 1119 | /// \pre init() must have been been called before using this function. |
|---|
| 1120 | Node mate(const Node& n) const |
|---|
| 1121 | { |
|---|
| 1122 | if (_matchingMap[n] == INVALID) |
|---|
| 1123 | return INVALID; |
|---|
| 1124 | else |
|---|
| 1125 | return _graph.oppositeNode(n, _matchingMap[n]); |
|---|
| 1126 | } |
|---|
| 1127 | |
|---|
| 1128 | /// \brief Return a const reference to the matching map. |
|---|
| 1129 | /// |
|---|
| 1130 | /// This function returns a const reference to a node map that stores |
|---|
| 1131 | /// the matching edge incident to each node. |
|---|
| 1132 | /// |
|---|
| 1133 | /// \pre init() must have been called before using this function. |
|---|
| 1134 | const MatchingMap& matchingMap() const |
|---|
| 1135 | { |
|---|
| 1136 | return _matchingMap; |
|---|
| 1137 | } |
|---|
| 1138 | |
|---|
| 1139 | /// \brief Checks whether the solution is optimal |
|---|
| 1140 | /// |
|---|
| 1141 | /// Checks using the dual solution whether the primal solution is optimal. |
|---|
| 1142 | /// |
|---|
| 1143 | /// \return \c true if the solution is optimal. |
|---|
| 1144 | bool checkOptimality() const |
|---|
| 1145 | { |
|---|
| 1146 | // We have to check three things: |
|---|
| 1147 | // - whether we really have a matching |
|---|
| 1148 | // - whether all nodes in X have a mate |
|---|
| 1149 | // - whether the labeling is feasible |
|---|
| 1150 | // |
|---|
| 1151 | // If all three conditions are satisfied then we know that we |
|---|
| 1152 | // have a maximum weight bipartite matching (Kuhn-Munkres theorem) |
|---|
| 1153 | |
|---|
| 1154 | // check whether all nodes in X have a mate |
|---|
| 1155 | // and that the two maps do not conflict |
|---|
| 1156 | for (int x = 0; x < _nX; x++) |
|---|
| 1157 | { |
|---|
| 1158 | if (!(_mateMapX[x] != -1 && _mateMapY[_mateMapX[x]] == x)) |
|---|
| 1159 | return false; |
|---|
| 1160 | } |
|---|
| 1161 | |
|---|
| 1162 | // every x should be linked to by exactly one y |
|---|
| 1163 | for (int x = 0; x < _nX; x++) |
|---|
| 1164 | { |
|---|
| 1165 | int n = 0; |
|---|
| 1166 | for (int y = 0; y < _nY; y++) |
|---|
| 1167 | { |
|---|
| 1168 | if (_mateMapY[y] == x) n++; |
|---|
| 1169 | } |
|---|
| 1170 | |
|---|
| 1171 | if (n != 1) return false; |
|---|
| 1172 | } |
|---|
| 1173 | |
|---|
| 1174 | // every y should be linked to by at most one x |
|---|
| 1175 | for (int y = 0; y < _nY; y++) |
|---|
| 1176 | { |
|---|
| 1177 | int n = 0; |
|---|
| 1178 | for (int x = 0; x < _nX; x++) |
|---|
| 1179 | { |
|---|
| 1180 | if (_mateMapX[x] == y) n++; |
|---|
| 1181 | } |
|---|
| 1182 | |
|---|
| 1183 | if (n > 1) return false; |
|---|
| 1184 | } |
|---|
| 1185 | |
|---|
| 1186 | // the labeling should be feasible |
|---|
| 1187 | for (int x = 0; x < _nX; x++) |
|---|
| 1188 | { |
|---|
| 1189 | for (int y = 0; y < _nY; y++) |
|---|
| 1190 | { |
|---|
| 1191 | if (!(_adjacencyMatrix[x][y].getWeight() <=_labelMapX[x] + _labelMapY[y])) |
|---|
| 1192 | return false; |
|---|
| 1193 | } |
|---|
| 1194 | } |
|---|
| 1195 | |
|---|
| 1196 | return true; |
|---|
| 1197 | } |
|---|
| 1198 | }; |
|---|
| 1199 | |
|---|
| 1200 | template<typename GR, typename WM> |
|---|
| 1201 | const typename WM::Value MaxWeightedDenseBipartiteMatching<GR, WM>::_maxValue = |
|---|
| 1202 | std::numeric_limits<typename WM::Value>::max(); |
|---|
| 1203 | |
|---|
| 1204 | template<typename GR, typename WM> |
|---|
| 1205 | const typename WM::Value MaxWeightedDenseBipartiteMatching<GR, WM>::_minValue = |
|---|
| 1206 | std::numeric_limits<typename WM::Value>::min(); |
|---|
| 1207 | } |
|---|
| 1208 | |
|---|
| 1209 | #endif //SSP_HUNGARIAN_H |
|---|