|
|
|
|
|
|
|
|
|
|
|
|
|
import * as util from '../../util'; |
|
|
|
|
|
let defaults = util.defaults({ |
|
expandFactor: 2, |
|
inflateFactor: 2, |
|
multFactor: 1, |
|
maxIterations: 20, |
|
attributes: [ |
|
function(edge) { |
|
return 1; |
|
} |
|
] |
|
}); |
|
|
|
|
|
let setOptions = ( options ) => defaults( options ); |
|
|
|
|
|
if( process.env.NODE_ENV !== 'production' ){ |
|
var printMatrix = function( M ) { |
|
let n = Math.sqrt(M.length); |
|
for ( let i = 0; i < n; i++ ) { |
|
let row = ''; |
|
for ( let j = 0; j < n; j++ ) { |
|
row += Number(M[i*n+j]).toFixed(3) + ' '; |
|
} |
|
console.log(row); |
|
} |
|
console.log(''); |
|
}; |
|
} |
|
|
|
|
|
let getSimilarity = function( edge, attributes ) { |
|
let total = 0; |
|
for ( let i = 0; i < attributes.length; i++ ) { |
|
total += attributes[i]( edge ); |
|
} |
|
return total; |
|
}; |
|
|
|
let addLoops = function( M, n, val ) { |
|
for (let i = 0; i < n; i++) { |
|
M[i * n + i] = val; |
|
} |
|
}; |
|
|
|
let normalize = function( M, n ) { |
|
let sum; |
|
for ( let col = 0; col < n; col++ ) { |
|
sum = 0; |
|
for ( let row = 0; row < n; row++ ) { |
|
sum += M[row * n + col]; |
|
} |
|
for ( let row = 0; row < n; row++ ) { |
|
M[row * n + col] = M[row * n + col] / sum; |
|
} |
|
} |
|
}; |
|
|
|
|
|
let mmult = function( A, B, n ) { |
|
let C = new Array( n * n ); |
|
|
|
for ( let i = 0; i < n; i++ ) { |
|
for ( let j = 0; j < n; j++ ) { |
|
C[i * n + j] = 0; |
|
} |
|
|
|
for ( let k = 0; k < n; k++ ) { |
|
for ( let j = 0; j < n; j++ ) { |
|
C[i * n + j] += A[i * n + k] * B[k * n + j]; |
|
} |
|
} |
|
} |
|
return C; |
|
}; |
|
|
|
let expand = function( M, n, expandFactor ) { |
|
let _M = M.slice(0); |
|
|
|
for ( let p = 1; p < expandFactor; p++ ) { |
|
M = mmult( M, _M, n ); |
|
} |
|
return M; |
|
}; |
|
|
|
let inflate = function( M, n, inflateFactor ) { |
|
let _M = new Array( n * n ); |
|
|
|
|
|
for ( let i = 0; i < n * n; i++ ) { |
|
_M[i] = Math.pow( M[i], inflateFactor ); |
|
} |
|
|
|
normalize( _M, n ); |
|
|
|
return _M; |
|
}; |
|
|
|
let hasConverged = function( M, _M, n2, roundFactor ) { |
|
|
|
for ( let i = 0; i < n2; i++ ) { |
|
let v1 = Math.round( M[i] * Math.pow(10, roundFactor) ) / Math.pow(10, roundFactor); |
|
let v2 = Math.round( _M[i] * Math.pow(10, roundFactor) ) / Math.pow(10, roundFactor); |
|
|
|
if ( v1 !== v2 ) { |
|
return false; |
|
} |
|
} |
|
return true; |
|
}; |
|
|
|
let assign = function( M, n, nodes, cy ) { |
|
let clusters = []; |
|
|
|
for ( let i = 0; i < n; i++ ) { |
|
let cluster = []; |
|
for ( let j = 0; j < n; j++ ) { |
|
|
|
if ( Math.round( M[i * n + j] * 1000 ) / 1000 > 0 ) { |
|
cluster.push( nodes[j] ); |
|
} |
|
} |
|
if ( cluster.length !== 0 ) { |
|
clusters.push( cy.collection(cluster) ); |
|
} |
|
} |
|
return clusters; |
|
}; |
|
|
|
let isDuplicate = function( c1, c2 ) { |
|
for (let i = 0; i < c1.length; i++) { |
|
if (!c2[i] || c1[i].id() !== c2[i].id()) { |
|
return false; |
|
} |
|
} |
|
return true; |
|
}; |
|
|
|
let removeDuplicates = function( clusters ) { |
|
|
|
for (let i = 0; i < clusters.length; i++) { |
|
for (let j = 0; j < clusters.length; j++) { |
|
if (i != j && isDuplicate(clusters[i], clusters[j])) { |
|
clusters.splice(j, 1); |
|
} |
|
} |
|
} |
|
return clusters; |
|
}; |
|
|
|
let markovClustering = function( options ) { |
|
let nodes = this.nodes(); |
|
let edges = this.edges(); |
|
let cy = this.cy(); |
|
|
|
|
|
let opts = setOptions( options ); |
|
|
|
|
|
let id2position = {}; |
|
for( let i = 0; i < nodes.length; i++ ){ |
|
id2position[ nodes[i].id() ] = i; |
|
} |
|
|
|
|
|
let n = nodes.length, n2 = n * n; |
|
let M = new Array( n2 ), _M; |
|
for ( let i = 0; i < n2; i++ ) { |
|
M[i] = 0; |
|
} |
|
|
|
for ( let e = 0; e < edges.length; e++ ) { |
|
let edge = edges[e]; |
|
let i = id2position[ edge.source().id() ]; |
|
let j = id2position[ edge.target().id() ]; |
|
|
|
let sim = getSimilarity( edge, opts.attributes ); |
|
|
|
M[i * n + j] += sim; |
|
M[j * n + i] += sim; |
|
} |
|
|
|
|
|
|
|
|
|
addLoops( M, n, opts.multFactor ); |
|
|
|
|
|
normalize( M, n ); |
|
|
|
let isStillMoving = true; |
|
let iterations = 0; |
|
while ( isStillMoving && iterations < opts.maxIterations ) { |
|
|
|
isStillMoving = false; |
|
|
|
|
|
_M = expand( M, n, opts.expandFactor ); |
|
|
|
|
|
M = inflate( _M, n, opts.inflateFactor ); |
|
|
|
|
|
if ( ! hasConverged( M, _M, n2, 4 ) ) { |
|
isStillMoving = true; |
|
} |
|
|
|
iterations++; |
|
} |
|
|
|
|
|
let clusters = assign( M, n, nodes, cy ); |
|
|
|
|
|
clusters = removeDuplicates( clusters ); |
|
|
|
return clusters; |
|
}; |
|
|
|
export default { |
|
markovClustering, |
|
mcl: markovClustering |
|
}; |
|
|