// -*- C++ -*- #ifndef HUGO_MAX_MATCHING_H #define HUGO_MAX_MATCHING_H ///\ingroup galgs ///\file ///\brief Maximum matching algorithm. #include #include #include namespace hugo { /// \addtogroup galgs /// @{ ///Maximum matching algorithms class. ///This class provides Edmonds' alternating forest matching ///algorithm. The starting matching (if any) can be passed to the ///algorithm using read-in functions \ref readNMapNode, \ref ///readNMapEdge or \ref readEMapBool depending on the container. The ///resulting maximum matching can be attained by write-out functions ///\ref writeNMapNode, \ref writeNMapEdge or \ref writeEMapBool ///depending on the preferred container. ///The dual side of a mathcing is a map of the nodes to ///MaxMatching::pos_enum, having values D, A and C showing the ///Gallai-Edmonds decomposition of the graph. The nodes in D induce ///a graph with factor-critical components, the nodes in A form the ///barrier, and the nodes in C induce a graph having a perfect ///matching. This decomposition can be attained by calling \ref ///writePos after running the algorithm. Before subsequent runs, ///the function \ref resetPos() must be called. ///\param Graph The undirected graph type the algorithm runs on. ///\author Jacint Szabo template class MaxMatching { typedef typename Graph::Node Node; typedef typename Graph::Edge Edge; typedef typename Graph::EdgeIt EdgeIt; typedef typename Graph::NodeIt NodeIt; typedef typename Graph::OutEdgeIt OutEdgeIt; typedef UnionFindEnum UFE; public: ///Indicates the Gallai-Edmonds decomposition of the graph. ///Indicates the Gallai-Edmonds decomposition of the graph, which ///shows an upper bound on the size of a maximum matching. The ///nodes with pos_enum \c D induce a graph with factor-critical ///components, the nodes in \c A form the canonical barrier, and the ///nodes in \c C induce a graph having a perfect matching. enum pos_enum { D=0, A=1, C=2 }; private: const Graph& G; typename Graph::template NodeMap mate; typename Graph::template NodeMap position; public: MaxMatching(const Graph& _G) : G(_G), mate(_G,INVALID), position(_G,C) {} ///Runs Edmonds' algorithm. ///Runs Edmonds' algorithm for sparse graphs (edgeNum >= ///2*nodeNum), and a heuristical Edmonds' algorithm with a ///heuristic of postponing shrinks for dense graphs. \pre Before ///the subsequent calls \ref resetPos must be called. inline void run(); ///Runs Edmonds' algorithm. ///If heur=0 it runs Edmonds' algorithm. If heur=1 it runs ///Edmonds' algorithm with a heuristic of postponing shrinks, ///giving a faster algorithm for dense graphs. \pre Before the ///subsequent calls \ref resetPos must be called. void runEdmonds( int heur ); ///Finds a greedy matching starting from the actual matching. ///Starting form the actual matching stored, it finds a maximal ///greedy matching. void greedyMatching(); ///Returns the size of the actual matching stored. ///Returns the size of the actual matching stored. After \ref ///run() it returns the size of a maximum matching in the graph. int size () const; ///Resets the map storing the Gallai-Edmonds decomposition. ///Resets the map storing the Gallai-Edmonds decomposition of the ///graph, making it possible to run the algorithm. Must be called ///before all runs of the Edmonds algorithm, except for the first ///run. void resetPos(); ///Resets the actual matching to the empty matching. ///Resets the actual matching to the empty matching. /// void resetMatching(); ///Reads a matching from a \c Node map of \c Nodes. ///Reads a matching from a \c Node map of \c Nodes. This map must be \e ///symmetric, i.e. if \c map[u]=v then \c map[v]=u must hold, and ///now \c uv is an edge of the matching. template void readNMapNode(NMapN& map) { NodeIt v; for( G.first(v); G.valid(v); G.next(v)) { mate.set(v,map[v]); } } ///Writes the stored matching to a \c Node map of \c Nodes. ///Writes the stored matching to a \c Node map of \c Nodes. The ///resulting map will be \e symmetric, i.e. if \c map[u]=v then \c ///map[v]=u will hold, and now \c uv is an edge of the matching. template void writeNMapNode (NMapN& map) const { NodeIt v; for( G.first(v); G.valid(v); G.next(v)) { map.set(v,mate[v]); } } ///Reads a matching from a \c Node map of \c Edges. ///Reads a matching from a \c Node map of incident \c Edges. This ///map must have the property that if \c G.bNode(map[u])=v then \c ///G.bNode(map[v])=u must hold, and now this edge is an edge of ///the matching. template void readNMapEdge(NMapE& map) { NodeIt v; for( G.first(v); G.valid(v); G.next(v)) { Edge e=map[v]; if ( G.valid(e) ) G.tail(e) == v ? mate.set(v,G.head(e)) : mate.set(v,G.tail(e)); } } ///Writes the matching stored to a \c Node map of \c Edges. ///Writes the stored matching to a \c Node map of incident \c ///Edges. This map will have the property that if \c ///G.bNode(map[u])=v then \c G.bNode(map[v])=u holds, and now this ///edge is an edge of the matching. template void writeNMapEdge (NMapE& map) const { typename Graph::template NodeMap todo(G,false); NodeIt v; for( G.first(v); G.valid(v); G.next(v)) { if ( mate[v]!=INVALID ) todo.set(v,true); } NodeIt e; for( G.first(e); G.valid(e); G.next(e)) { if ( todo[G.head(e)] && todo[G.tail(e)] ) { Node u=G.tail(e); Node v=G.head(e); if ( mate[u]=v && mate[v]=u ) { map.set(u,e); map.set(v,e); todo.set(u,false); todo.set(v,false); } } } } ///Reads a matching from an \c Edge map of \c bools. ///Reads a matching from an \c Edge map of \c bools. This map must ///have the property that there are no two adjacent edges \c e, \c ///f with \c map[e]=map[f]=true. The edges \c e with \c ///map[e]=true form the matching. template void readEMapBool(EMapB& map) { EdgeIt e; for( G.first(e); G.valid(e); G.next(e)) { if ( G.valid(e) ) { Node u=G.tail(e); Node v=G.head(e); mate.set(u,v); mate.set(v,u); } } } ///Writes the matching stored to an \c Edge map of \c bools. ///Writes the matching stored to an \c Edge map of \c bools. This ///map will have the property that there are no two adjacent edges ///\c e, \c f with \c map[e]=map[f]=true. The edges \c e with \c ///map[e]=true form the matching. template void writeEMapBool (EMapB& map) const { typename Graph::template NodeMap todo(G,false); NodeIt v; for( G.first(v); G.valid(v); G.next(v)) { if ( mate[v]!=INVALID ) todo.set(v,true); } NodeIt e; for( G.first(e); G.valid(e); G.next(e)) { map.set(e,false); if ( todo[G.head(e)] && todo[G.tail(e)] ) { Node u=G.tail(e); Node v=G.head(e); if ( mate[u]=v && mate[v]=u ) { map.set(e,true); todo.set(u,false); todo.set(v,false); } } } } ///Writes the canonical decomposition of the graph after running ///the algorithm. ///After calling any run methods of the class, and before calling ///\ref resetPos(), it writes the Gallai-Edmonds canonical ///decomposition of the graph. \c map must be a node map of \ref pos_enum 's. template void writePos (NMapEnum& map) const { NodeIt v; for( G.first(v); G.valid(v); G.next(v)) map.set(v,position[v]); } private: void lateShrink(Node v, typename Graph::template NodeMap& ear, UFE& blossom, UFE& tree); void normShrink(Node v, typename Graph::NodeMap& ear, UFE& blossom, UFE& tree); bool noShrinkStep(Node x, typename Graph::NodeMap& ear, UFE& blossom, UFE& tree, std::queue& Q); void shrinkStep(Node& top, Node& middle, Node& bottom, typename Graph::NodeMap& ear, UFE& blossom, UFE& tree, std::queue& Q); void augment(Node x, typename Graph::NodeMap& ear, UFE& blossom, UFE& tree); }; // ********************************************************************** // IMPLEMENTATIONS // ********************************************************************** template void MaxMatching::run() { if ( G.edgeNum() > 2*G.nodeNum() ) { greedyMatching(); runEdmonds(1); } else runEdmonds(0); } template void MaxMatching::runEdmonds( int heur=1 ) { typename Graph::template NodeMap ear(G,INVALID); //undefined for the base nodes of the blossoms (i.e. for the //representative elements of UFE blossom) and for the nodes in C typename UFE::MapType blossom_base(G); UFE blossom(blossom_base); typename UFE::MapType tree_base(G); UFE tree(tree_base); NodeIt v; for( G.first(v); G.valid(v); G.next(v) ) { if ( position[v]==C && mate[v]==INVALID ) { blossom.insert(v); tree.insert(v); position.set(v,D); if ( heur == 1 ) lateShrink( v, ear, blossom, tree ); else normShrink( v, ear, blossom, tree ); } } } template void MaxMatching::lateShrink(Node v, typename Graph::template NodeMap& ear, UFE& blossom, UFE& tree) { std::queue Q; //queue of the totally unscanned nodes Q.push(v); std::queue R; //queue of the nodes which must be scanned for a possible shrink while ( !Q.empty() ) { Node x=Q.front(); Q.pop(); if ( noShrinkStep( x, ear, blossom, tree, Q ) ) return; else R.push(x); } while ( !R.empty() ) { Node x=R.front(); R.pop(); OutEdgeIt e; for( G.first(e,x); G.valid(e); G.next(e) ) { Node y=G.bNode(e); if ( position[y] == D && blossom.find(x) != blossom.find(y) ) { //x and y must be in the same tree typename Graph::template NodeMap path(G,false); Node b=blossom.find(x); path.set(b,true); b=mate[b]; while ( b!=INVALID ) { b=blossom.find(ear[b]); path.set(b,true); b=mate[b]; } //going till the root Node top=y; Node middle=blossom.find(top); Node bottom=x; while ( !path[middle] ) shrinkStep(top, middle, bottom, ear, blossom, tree, Q); Node base=middle; top=x; middle=blossom.find(top); bottom=y; Node blossom_base=blossom.find(base); while ( middle!=blossom_base ) shrinkStep(top, middle, bottom, ear, blossom, tree, Q); blossom.makeRep(base); } // if shrink is needed while ( !Q.empty() ) { Node x=Q.front(); Q.pop(); if ( noShrinkStep(x, ear, blossom, tree, Q) ) return; else R.push(x); } } //for e } // while ( !R.empty() ) } template void MaxMatching::normShrink(Node v, typename Graph::NodeMap& ear, UFE& blossom, UFE& tree) { std::queue Q; //queue of the unscanned nodes Q.push(v); while ( !Q.empty() ) { Node x=Q.front(); Q.pop(); OutEdgeIt e; for( G.first(e,x); G.valid(e); G.next(e) ) { Node y=G.bNode(e); switch ( position[y] ) { case D: //x and y must be in the same tree if ( blossom.find(x) != blossom.find(y) ) { //shrink typename Graph::template NodeMap path(G,false); Node b=blossom.find(x); path.set(b,true); b=mate[b]; while ( b!=INVALID ) { b=blossom.find(ear[b]); path.set(b,true); b=mate[b]; } //going till the root Node top=y; Node middle=blossom.find(top); Node bottom=x; while ( !path[middle] ) shrinkStep(top, middle, bottom, ear, blossom, tree, Q); Node base=middle; top=x; middle=blossom.find(top); bottom=y; Node blossom_base=blossom.find(base); while ( middle!=blossom_base ) shrinkStep(top, middle, bottom, ear, blossom, tree, Q); blossom.makeRep(base); } break; case C: if ( mate[y]!=INVALID ) { //grow ear.set(y,x); Node w=mate[y]; blossom.insert(w); position.set(y,A); position.set(w,D); tree.insert(y); tree.insert(w); tree.join(y,blossom.find(x)); tree.join(w,y); Q.push(w); } else { //augment augment(x, ear, blossom, tree); mate.set(x,y); mate.set(y,x); return; } //if break; default: break; } } } } template void MaxMatching::greedyMatching() { NodeIt v; for( G.first(v); G.valid(v); G.next(v) ) if ( mate[v]==INVALID ) { OutEdgeIt e; for( G.first(e,v); G.valid(e); G.next(e) ) { Node y=G.bNode(e); if ( mate[y]==INVALID && y!=v ) { mate.set(v,y); mate.set(y,v); break; } } } } template int MaxMatching::size() const { int s=0; NodeIt v; for(G.first(v); G.valid(v); G.next(v) ) { if ( G.valid(mate[v]) ) { ++s; } } return (int)s/2; } template void MaxMatching::resetPos() { NodeIt v; for( G.first(v); G.valid(v); G.next(v)) position.set(v,C); } template void MaxMatching::resetMatching() { NodeIt v; for( G.first(v); G.valid(v); G.next(v)) mate.set(v,INVALID); } template bool MaxMatching::noShrinkStep(Node x, typename Graph::NodeMap& ear, UFE& blossom, UFE& tree, std::queue& Q) { OutEdgeIt e; for( G.first(e,x); G.valid(e); G.next(e) ) { Node y=G.bNode(e); if ( position[y]==C ) { if ( mate[y]!=INVALID ) { //grow ear.set(y,x); Node w=mate[y]; blossom.insert(w); position.set(y,A); position.set(w,D); tree.insert(y); tree.insert(w); tree.join(y,blossom.find(x)); tree.join(w,y); Q.push(w); } else { //augment augment(x, ear, blossom, tree); mate.set(x,y); mate.set(y,x); return true; } } } return false; } template void MaxMatching::shrinkStep(Node& top, Node& middle, Node& bottom, typename Graph::NodeMap& ear, UFE& blossom, UFE& tree, std::queue& Q) { ear.set(top,bottom); Node t=top; while ( t!=middle ) { Node u=mate[t]; t=ear[u]; ear.set(t,u); } bottom=mate[middle]; position.set(bottom,D); Q.push(bottom); top=ear[bottom]; Node oldmiddle=middle; middle=blossom.find(top); tree.erase(bottom); tree.erase(oldmiddle); blossom.insert(bottom); blossom.join(bottom, oldmiddle); blossom.join(top, oldmiddle); } template void MaxMatching::augment(Node x, typename Graph::NodeMap& ear, UFE& blossom, UFE& tree) { Node v=mate[x]; while ( G.valid(v) ) { Node u=ear[v]; mate.set(v,u); Node tmp=v; v=mate[u]; mate.set(u,tmp); } typename UFE::ItemIt it; for (tree.first(it,blossom.find(x)); tree.valid(it); tree.next(it)) { if ( position[it] == D ) { typename UFE::ItemIt b_it; for (blossom.first(b_it,it); blossom.valid(b_it); blossom.next(b_it)) { position.set( b_it ,C); } blossom.eraseClass(it); } else position.set( it ,C); } tree.eraseClass(x); } /// @} } //END OF NAMESPACE HUGO #endif //EDMONDS_H