#include "Matching.h" #include #include Matching::Matching(Graph & G): G(G), outer(2*G.GetNumVertices()), deep(2*G.GetNumVertices()), shallow(2*G.GetNumVertices()), tip(2*G.GetNumVertices()), active(2*G.GetNumVertices()), type(2*G.GetNumVertices()), forest(2*G.GetNumVertices()), root(2*G.GetNumVertices()), blocked(2*G.GetNumVertices()), dual(2*G.GetNumVertices()), slack(G.GetNumEdges()), mate(2*G.GetNumVertices()), m(G.GetNumEdges()), n(G.GetNumVertices()), visited(2*G.GetNumVertices()) { } void Matching::Grow() { Reset(); //All unmatched vertices will be roots in a forest that will be grown //The forest is grown by extending a unmatched vertex w through a matched edge u-v in a BFS fashion while(!forestList.empty()) { int w = outer[forestList.front()]; forestList.pop_front(); //w might be a blossom //we have to explore all the connections from vertices inside the blossom to other vertices for(int u : deep[w]) { int cont = false; for(int v : G.AdjList(u)) { if(IsEdgeBlocked(u, v)) continue; //u is even and v is odd if(type[outer[v]] == ODD) continue; //if v is unlabeled if(type[outer[v]] != EVEN) { //We grow the alternating forest int vm = mate[outer[v]]; forest[outer[v]] = u; type[outer[v]] = ODD; root[outer[v]] = root[outer[u]]; forest[outer[vm]] = v; type[outer[vm]] = EVEN; root[outer[vm]] = root[outer[u]]; if(!visited[outer[vm]]) { forestList.push_back(vm); visited[outer[vm]] = true; } } //If v is even and u and v are on different trees //we found an augmenting path else if(root[outer[v]] != root[outer[u]]) { Augment(u,v); Reset(); cont = true; break; } //If u and v are even and on the same tree //we found a blossom else if(outer[u] != outer[v]) { int b = Blossom(u,v); forestList.push_front(b); visited[b] = true; cont = true; break; } } if(cont) break; } } //Check whether the matching is perfect perfect = true; for(int i = 0; i < n; i++) if(mate[outer[i]] == -1) perfect = false; } bool Matching::IsAdjacent(int u, int v) { return (G.AdjMat()[u * G.GetNumVertices() + v] and not IsEdgeBlocked(u, v)); } bool Matching::IsEdgeBlocked(int u, int v) { return GREATER(slack[ G.GetEdgeIndex(u, v) ], 0); } bool Matching::IsEdgeBlocked(int e) { return GREATER(slack[e], 0); } //Vertices will be selected in non-decreasing order of their degree //Each time an unmatched vertex is selected, it is matched to its adjacent unmatched vertex of minimum degree void Matching::Heuristic() { vector degree(n, 0); BinaryHeap B; for(int i = 0; i < m; i++) { if(IsEdgeBlocked(i)) continue; pair p = G.GetEdge(i); int u = p.first; int v = p.second; degree[u]++; degree[v]++; } for(int i = 0; i < n; i++) B.Insert(degree[i], i); while(B.Size() > 0) { int u = B.DeleteMin(); if(mate[outer[u]] == -1) { int min = -1; for (int v : G.AdjList(u)) { if(IsEdgeBlocked(u, v) or (outer[u] == outer[v]) or (mate[outer[v]] != -1) ) continue; if(min == -1 or degree[v] < degree[min]) min = v; } if(min != -1) { mate[outer[u]] = min; mate[outer[min]] = u; } } } } //Destroys a blossom recursively void Matching::DestroyBlossom(int t) { if((t < n) or (blocked[t] and GREATER(dual[t], 0))) return; for(int s : shallow[t]) { outer[s] = s; for(int d : deep[s]) outer[d] = s; DestroyBlossom(s); } active[t] = false; blocked[t] = false; AddFreeBlossomIndex(t); mate[t] = -1; } void Matching::Expand(int start, bool expandBlocked = false) { std::stack Q; Q.push(start); while (!Q.empty()) { int u = Q.top(); Q.pop(); int v = outer[mate[u]]; int index = m; int p = -1, q = -1; //Find the regular edge {p,q} of minimum index connecting u and its mate //We use the minimum index to grant that the two possible blossoms u and v will use the same edge for a mate for (int di : deep[u]) { for (int dj : deep[v]) { if (IsAdjacent(di, dj) and G.GetEdgeIndex(di, dj) < index) { index = G.GetEdgeIndex(di, dj); p = di; q = dj; } } } mate[u] = q; mate[v] = p; //If u is a regular vertex, we are done if (u < n or (blocked[u] and not expandBlocked)) continue; bool found = false; //Find the position t of the new tip of the blossom for (list::iterator it = shallow[u].begin(); it != shallow[u].end() and not found; ) { int si = *it; for (vector::iterator jt = deep[si].begin(); jt != deep[si].end() and not found; ++jt) { if (*jt == p) found = true; } ++it; if (not found) { shallow[u].push_back(si); shallow[u].pop_front(); } } list::iterator it = shallow[u].begin(); //Adjust the mate of the tip mate[*it] = mate[u]; ++it; // //Now we go through the odd circuit adjusting the new mates while (it != shallow[u].end()) { list::iterator itnext = it; ++itnext; mate[*it] = *itnext; mate[*itnext] = *it; ++itnext; it = itnext; } //We update the sets blossom, shallow, and outer since this blossom is being deactivated for (int s : shallow[u]) { outer[s] = s; for (int d : deep[s]) outer[d] = s; } active[u] = false; AddFreeBlossomIndex(u); //Expand the vertices in the blossom for (int s : shallow[u]) { Q.push(s); } } } //Augment the path root[u], ..., u, v, ..., root[v] void Matching::Augment(int u, int v) { //We go from u and v to its respective roots, alternating the matching int p = outer[u]; int q = outer[v]; int outv = q; int fp = forest[p]; mate[p] = q; mate[q] = p; Expand(p); Expand(q); while(fp != -1) { q = outer[forest[p]]; p = outer[forest[q]]; fp = forest[p]; mate[p] = q; mate[q] = p; Expand(p); Expand(q); } p = outv; fp = forest[p]; while(fp != -1) { q = outer[forest[p]]; p = outer[forest[q]]; fp = forest[p]; mate[p] = q; mate[q] = p; Expand(p); Expand(q); } } void Matching::Reset() { for(int i = 0; i < 2*n; i++) { forest[i] = -1; root[i] = i; if(i >= n and active[i] and outer[i] == i) DestroyBlossom(i); } visited.assign(2*n, 0); forestList.clear(); for(int i = 0; i < n; i++) { if(mate[outer[i]] == -1) { type[outer[i]] = 2; if(!visited[outer[i]]) forestList.push_back(i); visited[outer[i]] = true; } else type[outer[i]] = 0; } } int Matching::GetFreeBlossomIndex() { int i = free.back(); free.pop_back(); return i; } void Matching::AddFreeBlossomIndex(int i) { free.push_back(i); } void Matching::ClearBlossomIndices() { free.clear(); for(int i = n; i < 2*n; i++) AddFreeBlossomIndex(i); } //Contracts the blossom w, ..., u, v, ..., w, where w is the first vertex that appears in the paths from u and v to their respective roots int Matching::Blossom(int u, int v) { int t = GetFreeBlossomIndex(); vector isInPath(2*n, false); //Find the tip of the blossom int u_ = u; while(u_ != -1) { isInPath[outer[u_]] = true; u_ = forest[outer[u_]]; } int v_ = outer[v]; while(not isInPath[v_]) v_ = outer[forest[v_]]; tip[t] = v_; //Find the odd circuit, update shallow, outer, blossom and deep //First we construct the set shallow (the odd circuit) list circuit; u_ = outer[u]; circuit.push_front(u_); while(u_ != tip[t]) { u_ = outer[forest[u_]]; circuit.push_front(u_); } shallow[t].clear(); deep[t].clear(); for(list::iterator it = circuit.begin(); it != circuit.end(); ++it) { shallow[t].push_back(*it); } v_ = outer[v]; while(v_ != tip[t]) { shallow[t].push_back(v_); v_ = outer[forest[v_]]; } //Now we construct deep and update outer for(int u_ : shallow[t]) { outer[u_] = t; for(int d : deep[u_]) { deep[t].push_back(d); outer[d] = t; } } forest[t] = forest[tip[t]]; type[t] = EVEN; root[t] = root[tip[t]]; active[t] = true; outer[t] = t; mate[t] = mate[tip[t]]; return t; } void Matching::UpdateDualCosts() { double e1 = 0, e2 = 0, e3 = 0; int inite1 = false, inite2 = false, inite3 = false; for(int i = 0; i < m; i++) { auto edge = G.GetEdge(i); int u = edge.first, v = edge.second; int outer_u = outer[u]; int outer_v = outer[v]; int type_u = type[outer_u]; int type_v = type[outer_v]; if( (type_u == EVEN and type_v == UNLABELED) or (type_v == EVEN and type_u == UNLABELED) ) { if(!inite1 or GREATER(e1, slack[i])) { e1 = slack[i]; inite1 = true; } } else if( (outer_u != outer_v) and type_u == EVEN and type_v == EVEN ) { if(!inite2 or GREATER(e2, slack[i])) { e2 = slack[i]; inite2 = true; } } } for(int i = n; i < 2*n; i++) { if(active[i] and i == outer[i] and type[outer[i]] == ODD and (!inite3 or GREATER(e3, dual[i]))) { e3 = dual[i]; inite3 = true; } } double e = 0; if(inite1) e = e1; else if(inite2) e = e2; else if(inite3) e = e3; if(GREATER(e, e2/2.0) and inite2) e = e2/2.0; if(GREATER(e, e3) and inite3) e = e3; for(int i = 0; i < 2*n; i++) { if(i != outer[i]) continue; if(active[i] and type[outer[i]] == EVEN) { dual[i] += e; } else if(active[i] and type[outer[i]] == ODD) { dual[i] -= e; } } for(int i = 0; i < m; i++) { auto edge = G.GetEdge(i); int u = edge.first, v = edge.second; int outer_u = outer[u]; int outer_v = outer[v]; int type_u = type[outer_u]; int type_v = type[outer_v]; if(outer_u != outer_v) { if(type_u == EVEN and type_v == EVEN) slack[i] -= 2.0*e; else if(type_u == ODD and type_v == ODD) slack[i] += 2.0*e; else if( (type_v == UNLABELED and type_u == EVEN) or (type_u == UNLABELED and type_v == EVEN) ) slack[i] -= e; else if( (type_v == UNLABELED and type_u == ODD) or (type_u == UNLABELED and type_v == ODD) ) slack[i] += e; } } for(int i = n; i < 2*n; i++) { if(GREATER(dual[i], 0)) { blocked[i] = true; } else if(active[i] and blocked[i]) { //The blossom is becoming unblocked if(mate[i] == -1) { DestroyBlossom(i); } else { blocked[i] = false; Expand(i); } } } } pair< list, double> Matching::SolveMinimumCostPerfectMatching(vector & cost) { SolveMaximumMatching(); if(!perfect) throw "Error: The graph does not have a perfect matching"; Clear(); //Initialize slacks (reduced costs for the edges) slack = cost; PositiveCosts(); //If the matching on the compressed graph is perfect, we are done perfect = false; while(not perfect) { //Run an heuristic maximum matching algorithm Heuristic(); //Grow a hungarian forest Grow(); UpdateDualCosts(); //Set up the algorithm for a new grow step Reset(); } list matching = RetrieveMatching(); double obj = 0; for(list::iterator it = matching.begin(); it != matching.end(); ++it) obj += cost[*it]; return pair< list, double >(matching, obj); } void Matching::PositiveCosts() { double minEdge = 0; for(int i = 0; i < m ;i++) if(GREATER(minEdge - slack[i], 0)) minEdge = slack[i]; for(int i = 0; i < m; i++) slack[i] -= minEdge; } list Matching::SolveMaximumMatching() { Clear(); Grow(); return RetrieveMatching(); } //Sets up the algorithm for a new run void Matching::Clear() { ClearBlossomIndices(); for(int i = 0; i < 2*n; i++) { outer[i] = i; deep[i].clear(); if(i Matching::RetrieveMatching() { list matching; for(int i = 0; i < 2*n; i++) if(active[i] and mate[i] != -1 and outer[i] == i) Expand(i, true); for(int i = 0; i < m; i++) { int u = G.GetEdge(i).first; int v = G.GetEdge(i).second; if(mate[u] == v) matching.push_back(i); } return matching; }