package emapse.graph;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import javolution.util.FastSet;
public class BipartiteGraph<T> extends SimpleGraph<T, BipartiteNode<T>> {
// First independent set in the bipartite graph
private Set<BipartiteNode<T>> U;
// Second independent set the bipartite graph
private Set<BipartiteNode<T>> V;
public BipartiteGraph() {
U = FastSet.newInstance();
V = FastSet.newInstance();
}
@Override
protected BipartiteNode<T> createNode(T element) {
return new BipartiteNode<T>(element);
}
/**
* Sets the first independent Set of the Bipartite graph. This method has to
* be called.
*/
public void setFirstIndependentSet(Collection<T> collection) {
Set<BipartiteNode<T>> Un = FastSet.newInstance();
for (T element : collection) {
if (nodes.get(element) != null)
Un.add(nodes.get(element));
}
U = Un;
}
/**
* Sets the second independent Set of the Bipartite graph. This method has
* to be called.
*/
public void setSecondIndependentSet(Collection<T> collection) {
Set<BipartiteNode<T>> Vn = FastSet.newInstance();
for (T element : collection) {
if (nodes.get(element) != null)
Vn.add(nodes.get(element));
}
V = Vn;
}
protected Set<BipartiteNode<T>> calculateMinimumCover() {
// Consider a bipartite graph where the vertices are partitioned into
// left (L) and right (R) sets.
// Suppose there is a maximum matching which partitions the edges into
// those used in the matching (Em) and those not (E0).
// Let B consist of all unmatched vertices from L(B1), as well as all
// vertices reachable from those by going left-to-right along edges from
// E0 (B2) and right-to-left along edges from Em (B3). This essentially
// means that for each unmatched Edge in L, we add into B all vertices
// that occur in a path alternating between edges from E0 and Em.
// Then (L minus B) union (R intersect B) is a minimum Edge cover.
Set<Edge<BipartiteNode<T>>> maximumMatching = calculateMaxmimumMatching();
Set<Edge<BipartiteNode<T>>> unmatchedEdges = edges;
unmatchedEdges.removeAll(maximumMatching);
// Koing's theorem: if U,BipartiteNode<T> are the left and right
// independent sets of
// the bipartite graph, and T the unmatched vertices of U + the vertices
// that belong to an alternative path of the unmatched and matched
// vertices, then the minimum Edge Set is
// (U\T)Union (BipartiteNode<T> Intersection T)
Set<BipartiteNode<T>> matchedVertices = FastSet.newInstance();
for (Edge<BipartiteNode<T>> Edge : maximumMatching) {
matchedVertices.add(Edge.getSource());
matchedVertices.add(Edge.getTarget());
}
// calculates unmatched vertices from L
Collection<BipartiteNode<T>> unmatchedVertices = nodes.values();
unmatchedVertices.removeAll(matchedVertices);
unmatchedVertices.retainAll(U);
Set<BipartiteNode<T>> B1 = FastSet.newInstance();
B1.addAll(unmatchedVertices);
Set<BipartiteNode<T>> B2 = FastSet.newInstance();
B2.addAll(B1);
while (true) {
Set<BipartiteNode<T>> B3 = FastSet.newInstance();
for (Edge<BipartiteNode<T>> edge : unmatchedEdges) {
if (B2.contains(edge.getSource()))
B3.add(edge.getTarget());
}
B3.removeAll(B1);
if (B3.isEmpty())
break;
B1.addAll(B3);
Set<BipartiteNode<T>> B4 = FastSet.newInstance();
for (Edge<BipartiteNode<T>> edge : maximumMatching) {
if (B3.contains(edge.getTarget()))
B4.add(edge.getSource());
}
B4.removeAll(B1);
if (B4.isEmpty())
break;
B1.addAll(B4);
B2 = B4;
}
Set<BipartiteNode<T>> leftClone = new FastSet(U);
Set<BipartiteNode<T>> rightClone = new FastSet(V);
leftClone.removeAll(B1);
rightClone.retainAll(B1);
leftClone.addAll(rightClone);
return leftClone;
}
public Set<Edge<BipartiteNode<T>>> calculateMaxmimumMatching() {
for (BipartiteNode<T> node : U) {
node.matching = null;
}
for (BipartiteNode<T> node : V) {
node.matching = null;
}
while (true) {
// Find all potential beginnings of shortest augmenting paths
Set<BipartiteNode<T>> B = FastSet.newInstance();
for (BipartiteNode<T> u : U) {
if (u.matching == null)
B.add(u);
}
// Find all endings of shortest augmenting paths
Set<BipartiteNode<T>> F = findAugmentingPathEndings(B);
Set<Edge<BipartiteNode<T>>> M = FastSet.newInstance();
if (F.isEmpty()) {
M = FastSet.newInstance();
for (BipartiteNode<T> u : U) {
if (u.matching != null)
M.add(new Edge<BipartiteNode<T>>(u, u.matching));
}
return M;
}
augmentPaths(F);
}
}
private Set<BipartiteNode<T>> findAugmentingPathEndings(
Set<BipartiteNode<T>> B) {
// Perform BFS starting with vertices of B.
// Return: a list of free vertices which potentially
// end the shortest Edge-disjoint augmenting paths.
// Side effect: For any Edge v.layer has the index of
// the BFS layer to reach this Edge (or -1)
// B: potential beginning of augmenting paths
Set<BipartiteNode<T>> Q = new FastSet(B);
// F: The set of free endings (the other side of augmenting paths)
Set<BipartiteNode<T>> F = FastSet.newInstance();
for (BipartiteNode<T> node : U) {
node.layer = -1;
}
for (BipartiteNode<T> node : V) {
node.layer = -1;
}
// The potential beginnings of augmenting paths are at layer 0.
for (BipartiteNode<T> node : B) {
node.layer = 0;
}
while (!Q.isEmpty()) {
Set<BipartiteNode<T>> G = FastSet.newInstance();
for (BipartiteNode<T> q : Q) {
for (Edge<BipartiteNode<T>> v : edges) {
if (v.getSource() == q || v.getTarget() == q) {
BipartiteNode<T> p;
if (v.getSource() == q)
p = v.getTarget();
else
// (v.getTarget() == q)
p = v.getSource();
if (p.layer == -1) {
p.layer = q.layer + 1;
if (p.matching == null)
// Collect the free Edge, continue BFS
F.add(p);
else
G.add(p);
}
}
}
}
if (!F.isEmpty())
// Free vertices found, stop at this layer.
return F;
Q.clear();
for (BipartiteNode<T> q : G) {
// By construction, q matched.
BipartiteNode<T> p = q.matching;
p.layer = q.layer + 1;
Q.add(p);
}
}
return FastSet.newInstance();
}
private void augmentPaths(Set<BipartiteNode<T>> F) {
for (BipartiteNode<T> node : U) {
node.visited = false;
}
for (BipartiteNode<T> node : V) {
node.visited = false;
}
for (BipartiteNode<T> q : F) {
List<Edge<BipartiteNode<T>>> P = getAugmentingPath(q);
// Make sure q ends a Edge-disjoint augmenting path
if (!P.isEmpty()) {
// Perform M := M + P
boolean odd = true;
for (Edge<BipartiteNode<T>> v : P) {
BipartiteNode<T> t = v.getSource();
BipartiteNode<T> qprime = v.getTarget();
if (odd) {
// Make (t,q) match (first, third, ... edges)
// System.out.println("layer"+qprime.layer+t.layer);
t.matching = qprime;
qprime.matching = t;
odd = false;
} else {
// System.out.println("layer"+qprime.layer+t.layer);
// t.matching has already been updated by the odd
// condition
// This section is not needed entirely. Only the part
// inside the else is needed
if (U.contains(qprime))
qprime.matching = null;
else
// This is not strictly required
t.matching = null;
odd = true;
}
}
}
}
}
// f is always in the girls group
private List<Edge<BipartiteNode<T>>> getAugmentingPath(BipartiteNode<T> f) {
if (f.visited || f.layer == 0)
return new ArrayList<Edge<BipartiteNode<T>>>();
f.visited = true;
// Consider only edges going one layer backwards.
for (Edge<BipartiteNode<T>> v : edges) {
if (v.getSource() == f || v.getTarget() == f) {
BipartiteNode<T> q;
BipartiteNode<T> p;
if (v.getTarget() == f) {
p = v.getTarget();
q = v.getSource();
} else {
// (v.getSource() == f)
p = v.getSource();
q = v.getTarget();
}
if (p.layer == q.layer + 1) {
if (q.layer == 0 && q.visited == false) {
// q is at an even layer
q.visited = true;
List<Edge<BipartiteNode<T>>> result = new ArrayList<Edge<BipartiteNode<T>>>();
result.add(v);
return result;
}
// Note that q.matching must be defined, and point to
// the previous layer. This is due to BFS traversal order.
List<Edge<BipartiteNode<T>>> P = getAugmentingPath(q.matching);
if (!P.isEmpty()) {
// Append (q, p) to the path found so far.
P.add(new Edge<BipartiteNode<T>>(q, q.matching));
P.add(v);
return P;
}
}
}
}
return new ArrayList<Edge<BipartiteNode<T>>>();
}
}