/**
* %SVN.HEADER%
*/
package medusa.georgios.enhanced_mcl;
/**
* MarkovClustering implements the Markov clustering (MCL) algorithm for graphs,
* using a HashMap-based sparse representation of a Markov matrix, i.e., an
* adjacency matrix m that is normalised to one. Elements in a column / node can
* be interpreted as decision probabilities of a random walker being at that
* node. Note: whereas we explain the algorithms with columns, the actual
* implementation works with rows because the used sparse matrix has row-major
* format.
*
* The basic idea underlying the MCL algorithm is that dense regions in sparse
* graphs correspond with regions in which the number of k-length paths is
* relatively large, for small k in N, which corresponds to multiplying
* probabilities in the matrix appropriately. Random walks of length k have
* higher probability (product) for paths with beginning and ending in the same
* dense region than for other paths.
*
* The algorithm starts by creating a Markov matrix from the graph, for which
* first the adjacency matrix is added diagonal elements to include self-loops
* for all nodes, i.e., probabilities that the random walker stays at a
* particular node. After this initialisation, the algorithm works by
* alternating two operations, expansion and inflation, iteratively recomputing
* the set of transition probabilities. The expansion step corresponds to matrix
* multiplication (on stochastic matrices), the inflation step corresponds with
* a parametrized inflation operator Gamma_r, which acts column-wise on (column)
* stochastic matrices (here, we use row-wise operation, which is analogous).
*
* The inflation operator transforms a stochastic matrix into another one by
* raising each element to a positive power p and re-normalising columns to keep
* the matrix stochastic. The effect is that larger probabilities in each column
* are emphasised and smaller ones deemphasised. On the other side, the matrix
* multiplication in the expansion step creates new non-zero elements, i.e.,
* edges. The algorithm converges very fast, and the result is an idempotent
* Markov matrix, M = M * M, which represents a hard clustering of the graph
* into components.
*
* Expansion and inflation have two opposing effects: While expansion flattens
* the stochastic distributions in the columns and thus causes paths of a random
* walker to become more evenly spread, inflation contracts them to favoured
* paths.
*
* Description is based on the introduction of Stijn van Dongen's thesis Graph
* Clustering by Flow Simulation (2000); for a mathematical treatment of the
* algorithm and the associated MCL process, see there.
*
* @author gregor :: arbylon . net
*/
public class MarkovClustering {
public static List2Graph g;
int clusters=0;
String report;
/**
* run the MCL process.
*
* @param a
* matrix
* @param maxResidual
* maximum difference between row elements and row square sum
* (measure of idempotence)
* @param pGamma
* inflation exponent for Gamma operator
* @param loopGain
* values for cycles
* @param maxZero
* maximum value considered zero for pruning operations
* @return the resulting matrix
*/
public String getReport()
{
return this.report;
}
public void setReport(String str)
{
this.report=str;
}
public SparseMatrix run(SparseMatrix a, double maxResidual, double pGamma, double loopGain, double maxZero) {
// add cycles
addLoops(a, loopGain);
a.normaliseRows();
double residual = 1.;
int i = 0;
// main iteration
while (residual > maxResidual) {
i++;
a = expand(a);
residual = inflate(a, pGamma, maxZero);
}
return a;
}
/**
* inflate stochastic matrix by Hadamard (elementwise) exponentiation,
* pruning and normalisation :
*
* result = Gamma ( m, p ) = normalise ( prune ( m .^ p ) ).
*
* By convention, normalisation is done along rows (SparseMatrix has
* row-major representation)
*
* @param m
* matrix (mutable)
* @param p
* exponent as a double
* @param zeromax
* below which elements are pruned from the sparse matrix
* @return residuum value, m is modified.
*/
public double inflate(SparseMatrix m, double p, double zeromax) {
double res = 0.;
// matlab: m = m .^ p
m.hadamardPower(p);
// matlab: m(find(m < threshold)) = 0
m.prune(zeromax);
// matlab [for cols]: dinv = diag(1./sum(m)); m = m * dinv; return
// sum(m)
SparseVector rowsums = m.normalise(1.);
// check if done: if the maximum element
for (int i : rowsums.keySet()) {
SparseVector row = m.get(i);
double max = row.max();
double sumsq = row.sum(2.);
res = Math.max(res, max - sumsq);
}
return res;
}
/**
* expand stochastic quadratic matrix by sqaring it with itself: result = m *
* m. Here normalisation is rowwise.
*
* @param matrix
* @return new matrix (pointer != argument)
*/
public SparseMatrix expand(SparseMatrix m) {
m = m.times(m);
return m;
}
/**
* add loops with specific energy, which corresponds to adding loopGain to
* the diagonal elements.
*
* @param a
* @param loopGain
*/
private void addLoops(SparseMatrix a, double loopGain) {
if (loopGain <= 0) {
return;
}
for (int i = 0; i < a.size(); i++) {
a.add(i, i, loopGain);
}
}
}
Ads help maintain this website.