package vroom.common.graphs;
import java.util.Iterator;
import vroom.common.utilities.graphs.CompleteGraph;
import vroom.common.utilities.graphs.Cut;
import vroom.common.utilities.graphs.Path;
/**
* The Class<code>MaxFlowFordFukerson</code> is an implementation of the Ford Fulkerson max flow / min cut algorithm
* <p>
* Creation date: 14 août 2010 - 20:19:18.
*
* @author Victor Pillac <br/>
* <a href="http://uniandes.edu.co">Universidad de Los Andes</a>-<a href="http://copa.uniandes.edu.co">Copa</a> <br/>
* <a href="http://www.emn.fr">Ecole des Mines de Nantes</a>-<a
* href="http://www.irccyn.ec-nantes.fr/irccyn/d/en/equipes/Slp">SLP</a>
* @version 1.0
*/
public class MaxFlowFordFulkerson {
public static double ZERO_TOLERANCE = 1e-10;
/** The m flow. */
private final double[][] mFlow;
/** The m residual. */
private final double[][] mResidual;
/** The m graph. */
private final CompleteGraph mGraph;
/** The m current cut. */
private Cut mCurrentCut;
/** The m current max flow. */
private double mCurrentMaxFlow;
/**
* Creates a new <code>MaxFlowFordFulkerson</code>. algorithm
*
* @param graph
* the graph on which calculation will be made
*/
public MaxFlowFordFulkerson(CompleteGraph graph) {
super();
mGraph = graph;
mFlow = new double[mGraph.getNodeCount()][mGraph.getNodeCount()];
mResidual = new double[mGraph.getNodeCount()][mGraph.getNodeCount()];
for (int i = 0; i < mGraph.getNodeCount(); i++) {
for (int j = 0; j < mGraph.getNodeCount(); j++) {
mResidual[i][j] = mGraph.getArcCapacity(i, j);
}
}
}
/**
* Min cut algorithm.
*
* @param s
* the source node
* @param t
* the sink node
* @return the min cut between <code>s</code> and <code>t</code>
*/
public Cut minCut(int s, int t) {
mCurrentMaxFlow = 0;
mCurrentCut = new Cut(mGraph);
Path improvingPath = improvingPath(s, t);
while (improvingPath != null) {
// System.out.println("Improving Path: "+improvingPath);
mCurrentCut = new Cut(mGraph);
// Update current max flow
mCurrentMaxFlow += improvingPath.getFlow();
Iterator<Integer> it = improvingPath.iterator();
int prev = it.next();
int next;
while (it.hasNext()) {
next = it.next();
// Update current flow and residual
mFlow[prev][next] += improvingPath.getFlow();
mFlow[next][prev] -= improvingPath.getFlow();
mResidual[prev][next] -= improvingPath.getFlow();
mResidual[next][prev] += improvingPath.getFlow();
prev = next;
}
improvingPath = improvingPath(s, t);
}
return mCurrentCut;
}
/**
* Max flow algorithm
*
* @param s
* the source node
* @param t
* the sink node
* @return the max flow between the <code>s</code> and <code>t</code>
*/
public double maxFlow(int s, int t) {
minCut(s, t);
return mCurrentMaxFlow;
}
/**
* Gets the current flow.
*
* @param i
* @param j
* @return the current flow for arc (i,j)
*/
public double getCurrentFlow(int i, int j) {
return mFlow[i][j];
}
/**
* Gets the max flow from the last call to {@link #maxFlow(int, int)} or {@link #minCut(int, int)}
*
* @return the previously calculated max flow
*/
public double getCurrentMaxFlow() {
return mCurrentMaxFlow;
}
/**
* Gets the min cut from the last call to {@link #maxFlow(int, int)} or {@link #minCut(int, int)}
*
* @return the previously calculated min cut
*/
public Cut getCurrentMinCut() {
return mCurrentCut;
}
/**
* Gets the current residual value.
*
* @param i
* @param j
* @return the current residual for arc (i,j)
*/
public double getCurrentResidual(int i, int j) {
return mResidual[i][j];
}
/**
* Improving path.
*
* @param s
* the s
* @param t
* the t
* @return the path
*/
public Path improvingPath(int s, int t) {
mCurrentCut = new Cut(mGraph);
Path path = new Path(mGraph);
path.append(s);
mCurrentCut.add(s);
path.setFlow(Double.POSITIVE_INFINITY);
double flow = improvingPathRec(path, t);
if (flow > 0) {
return path;
} else {
return null;
}
}
/**
* Improving path rec.
*
* @param s
* the s
* @param t
* the t
* @param path
* the path
* @return the double
*/
protected double improvingPathRec(Path path, int t) {
// Reached the sink node
if (path.getLast() == t) {
return path.getFlow();
}
// System.out.println("Cut "+mCurrentCut);
// System.out.println("Comp "+mCurrentCut.getComplement());
for (int j : mCurrentCut.getComplement()) {
// System.out.printf("(%s,%s)=%s\n",path.getLast(),j,mResidual[path.getLast()][j]);
if (mResidual[path.getLast()][j] > ZERO_TOLERANCE) {
mCurrentCut.add(j);
double maxFlowBck = path.getFlow();
// Append the node j and update the flow
if (mResidual[path.getLast()][j] < path.getFlow()) {
path.setFlow(mResidual[path.getLast()][j]);
}
path.append(j);
// Recursively find and improving path
double maxFlow = improvingPathRec(path, t);
if (maxFlow > 0) {
// Return the value of the improving path
return maxFlow;
} else {
// Reset the path
path.pop();
path.setFlow(maxFlowBck);
}
}
}
return 0;
}
/**
* Reset the algorithm data structures.
*/
public void reset() {
for (int i = 0; i < mFlow.length; i++) {
for (int j = 0; j < mFlow.length; j++) {
mFlow[i][j] = 0;
mResidual[i][j] = mGraph.getArcCapacity(i, j);
mCurrentCut = null;
mCurrentMaxFlow = 0;
}
}
}
}