osci-render/Source/chinese_postman/Matching.cpp

619 wiersze
12 KiB
C++

#include "Matching.h"
#include <deque>
#include <stack>
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<int> degree(n, 0);
BinaryHeap B;
for(int i = 0; i < m; i++)
{
if(IsEdgeBlocked(i)) continue;
pair<int, int> 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<int> 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<int>::iterator it = shallow[u].begin(); it != shallow[u].end() and not found; )
{
int si = *it;
for (vector<int>::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<int>::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<int>::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<bool> 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<int> 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<int>::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<int>, double> Matching::SolveMinimumCostPerfectMatching(vector<double> & 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<int> matching = RetrieveMatching();
double obj = 0;
for(list<int>::iterator it = matching.begin(); it != matching.end(); ++it)
obj += cost[*it];
return pair< list<int>, 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<int> 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<n)
deep[i].push_back(i);
shallow[i].clear();
if(i < n) active[i] = true;
else active[i] = false;
type[i] = 0;
forest[i] = -1;
root[i] = i;
blocked[i] = false;
dual[i] = 0;
mate[i] = -1;
tip[i] = i;
}
slack.assign(m, 0);
}
list<int> Matching::RetrieveMatching()
{
list<int> 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;
}