package graphtea.extensions.reports.spectralreports.maxflowmincut;
import graphtea.graph.graph.Edge;
import graphtea.graph.graph.GraphModel;
import graphtea.graph.graph.Vertex;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
/***
*
* @author Hooman Mohajeri Moghaddam
* This is the implementation of the Push-Relabel algorithm by Goldberg
* For further detail refer to Introduction to Algorithms (Chapter 26)
* Throughout the class the Graph is considered connected and the weights non-negative.
*/
public class PushRelabel extends MaxFlow{
private int height[];
private int excess[];
private Integer current[];
private HashMap <Integer, LinkedList<Integer>> neighborMap ;
private HashMap <Integer, Iterator<Integer>> iterMap ;
private boolean isDirecte;
public PushRelabel(GraphModel g, Vertex source, Vertex sink, boolean showResult)
{
super(g,source,sink,showResult);
isDirecte = g.isDirected();
C = g.getWeightedAdjacencyMatrix().getArrayCopy();
n = C.length;
current = new Integer[n];
// a map between the vertex id and a linked-list of simple(undirected) neighbors".
neighborMap = new HashMap<>();
iterMap = new HashMap<>();
//initializing neighbor list
for(Vertex v: g)
{
LinkedHashSet<Integer> simpleNeihbors = new LinkedHashSet<>();
for (Vertex n: g.getNeighbors(v))
simpleNeihbors.add(n.getId());
for (Vertex n: g.getBackNeighbours(v))
simpleNeihbors.add(n.getId());
LinkedList<Integer> neighbors = new LinkedList<>(simpleNeihbors);
int id = v.getId();
neighborMap.put(id, neighbors);
// iterMap.put(id , neighbors.iterator());
// if(!neighbors.isEmpty())
// current[id]=neighbors.getFirst();
}
}
public void doAlgorithm()
{
relableToFront();
}
private void initializePreflow()
{
F = new double[n][n];
height = new int[n];
excess = new int[n];
current = new Integer[n];
this.t = sink.getId();
this.s = source.getId();
height[s] = n;
LinkedList<Integer> neighbors;
for(int k=0; k<n ; k++)
{
neighbors = neighborMap.get(k);
iterMap.put(k , neighbors.listIterator(0));
if(!neighbors.isEmpty())
current[k]=neighbors.getFirst();
}
for(int v : neighborMap.get(s))
{
F[s][v]=C[s][v];
excess[v]=(int)C[s][v];
excess[s]-=C[s][v];
}
//makeLabels();
}
private void relabel(int u)
{
LinkedList<Integer> neighbors = neighborMap.get(u);
int min = Integer.MAX_VALUE;
for(Integer v : neighbors)
{
if( Cf(u, v) > 0 && height[v] < min )
min = height[v];
}
height[u] = 1+min;
//makeLabels();
}
private void push(int u , int v)
{
int deltaF = (excess[u] < Cf(u,v) ? excess[u] : Cf(u,v));
if(C[u][v]>0)
F[u][v] += deltaF;
else
F[v][u] -= deltaF;
excess[u] -= deltaF;
excess[v] += deltaF;
//makeLabels();
}
private int Cf(int u, int v)
{
return (int)(C[u][v] - F[u][v] + F[v][u]);
}
private void discharge(int u)
{
Iterator<Integer> iter = iterMap.get(u);
Integer v;
while (excess[u] > 0)
{
v = current[u];
if (v==null)
{
relabel(u);
iter=neighborMap.get(u).listIterator(0);
iterMap.put(u, iter);
current[u] = iter.next();
}
else if(Cf(u,v)>0 && height[u]==height[v]+1)
push(u,v);
else
{
if(iter.hasNext())
current[u]=iter.next();
else
current[u] = null;
}
}
}
private void relableToFront()
{
initializePreflow();
LinkedList<Integer> L = new LinkedList<>();
for(Integer i=0 ; i < n ; i++)
if( i != s && i != t )
L.add(i);
Iterator<Integer> iter = L.iterator();
int oldHeight;
while(iter.hasNext())
{
int u = iter.next();
oldHeight = height[u];
discharge(u);
if (height[u] > oldHeight)
{
iter.remove();
L.addFirst(u);
iter=L.listIterator(0);
iter.next();
}
}
}
protected void makeLabels()
{
int s,t;
for(Edge e : g.getEdges())
{
s = e.source.getId();
t = e.target.getId();
if(isDirecte)
e.setLabel(F[s][t] + "/" + C[s][t] );
else
{
if(s<t)
e.setLabel((F[s][t] - F[t][s]) + "/" + C[s][t] );
else
e.setLabel((F[t][s] - F[s][t]) + "/" + C[s][t] );
}
e.setShowWeight(false);
}
for(Vertex v : g)
//v.setLabel(""+height[v.getId()]);
g.setDrawEdgeLabels(true);
}
}