From 11e711b1485d1cf6d9c9f1dfb06a64dbf69b6010 Mon Sep 17 00:00:00 2001 From: Jean-Daniel Fekete Date: Fri, 24 Apr 2015 15:47:17 -0400 Subject: [PATCH] added adjacent_exchange pass for barycenter heuristic --- examples/matrix.js | 7 +++- reorder.v1.js | 66 ++++++++++++++---------------- reorder.v1.min.js | 5 ++- src/adjacent_exchange.js | 87 ++++++++++++++++++++++++++++++++++++++++ src/barycenter.js | 19 +++------ src/count_crossings.js | 8 ++-- src/graph.js | 4 -- test/barycenter-test.js | 25 ++++++++---- 8 files changed, 153 insertions(+), 68 deletions(-) create mode 100644 src/adjacent_exchange.js diff --git a/examples/matrix.js b/examples/matrix.js index 65c6c07..f92ec0f 100644 --- a/examples/matrix.js +++ b/examples/matrix.js @@ -72,9 +72,12 @@ function matrix(json) { } function computeBarycenter() { - var barycenter = reorder.barycenter(graph); + var barycenter = reorder.barycenter(graph), + improved = reorder.adjacent_exchange(graph, + barycenter[0], + barycenter[1]); - barycenter[0].forEach(function(lo, i) { + improved[0].forEach(function(lo, i) { nodes[i].barycenter = lo; }); diff --git a/reorder.v1.js b/reorder.v1.js index c77fb3f..0e4533f 100644 --- a/reorder.v1.js +++ b/reorder.v1.js @@ -469,8 +469,6 @@ reorder.graph = function(nodes, links, directed) { graph.sinks = function() { var sinks = [], i; - if (! directed) - return reorder.range(nodes.length); for (i = 0; i < nodes.length; i++) { if (outEdges(i).length == 0) @@ -482,8 +480,6 @@ reorder.graph = function(nodes, links, directed) { graph.sources = function() { var sources = [], i; - if (! directed) - return reorder.range(nodes.length); for (i = 0; i < nodes.length; i++) { if (inEdges(i).length == 0) @@ -796,13 +792,14 @@ function count_crossings(graph, north, south) { firstIndex, treeSize, tree, index, weightSum, invert = false, crosscount; + var comp = reorder.permutation(graph.nodes().length); + if (north==undefined) { - var comp = reorder.permutation(graph.nodes().length); north = comp.filter(function(n) { - return graph.outEdges(n).length!=0; + return graph.outDegree(n) != 0; }), south = comp.filter(function(n) { - return graph.inEdges(n).length!=0; + return graph.inDegree(n) != 0; }); } @@ -853,6 +850,7 @@ function count_crossings(graph, north, south) { } return crosscount; } + reorder.count_crossings = count_crossings; // Accorging to // E. R. Gansner, E. Koutsofios, S. C. North, and K.-P. Vo. 1993. A @@ -885,36 +883,40 @@ function count_out_crossings(graph, v, w, inv) { iv, iw, p0, cross = 0; for (iw = 0; iw < w_edges.length; iw++) { - p0 = inv[w_edges[iw].target.index]; + p0 = inv[w_edges[iw].source.index]; for (iv = 0; iv < v_edges.length; iv++) { - if (inv[v_edges[iv].target.index] > p0) + if (inv[v_edges[iv].source.index] > p0) cross++; } } return cross; } -function adjacent_exchange(graph, layer1, layer2, crossings) { +function adjacent_exchange(graph, layer1, layer2) { + layer1 = layer1.slice(); + layer2 = layer2.slice(); var i, v, w, c0, c1, inv_layer1 = inverse_permutation(layer1), inv_layer2 = inverse_permutation(layer2), - swapped = true; - + swapped = true, + improved = 0; + while (swapped) { swapped = false; for (i = 0; i < layer1.length-1; i++) { v = layer1[i]; w = layer1[i+1]; + // should reduce the in crossing and the out crossing + // otherwise what we gain horizontally is lost vertically c0 = count_out_crossings(graph, v, w, inv_layer2); c1 = count_out_crossings(graph, w, v, inv_layer2); - if (c1 < c0) { + if (c0 > c1) { layer1[i] = w; layer1[i+1] = v; - c0 = inv_layer1[v]; - inv_layer1[v] = inv_layer1[w]; - inv_layer1[w] = c0; + inv_layer1[w] = i; + inv_layer1[v] = i+1; swapped = true; - crossings -= c0-c1; + improved += c0 - c1; } } for (i = 0; i < layer2.length-1; i++) { @@ -922,19 +924,18 @@ function adjacent_exchange(graph, layer1, layer2, crossings) { w = layer2[i+1]; c0 = count_in_crossings(graph, v, w, inv_layer1); c1 = count_in_crossings(graph, w, v, inv_layer1); - if (c1 < c0) { + if (c0 > c1) { layer2[i] = w; layer2[i+1] = v; - c0 = inv_layer2[v]; - inv_layer2[v] = inv_layer2[w]; - inv_layer2[w] = c0; + inv_layer2[w] = i; + inv_layer2[v] = i+1; swapped = true; - crossings -= c0-c1; + improved += c0 - c1; } } } - return [layer1, layer2, crossings]; + return [layer1, layer2, improved]; }; reorder.adjacent_exchange = adjacent_exchange; @@ -984,10 +985,10 @@ reorder.barycenter1 = function(graph, comp, max_iter) { i, v, neighbors; layer1 = comp.filter(function(n) { - return graph.outEdges(n).length!=0; + return graph.outDegree(n) != 0; }); layer2 = comp.filter(function(n) { - return graph.inEdges(n).length!=0; + return graph.inDegree(n) != 0; }); if (comp.length < 3) { return [layer1, layer2, @@ -999,19 +1000,12 @@ reorder.barycenter1 = function(graph, comp, max_iter) { else if ((max_iter%2)==1) max_iter++; // want even number of iterations - layer1 = comp.filter(function(n) { - return graph.outEdges(n).length!=0; - }); - layer2 = comp.filter(function(n) { - return graph.inEdges(n).length!=0; - }); - for (i = 0; i < layer2.length; i++) nodes[layer2[i]].pos = i; best_crossings = count_crossings(graph, layer1, layer2); - best_layer1 = layer1; - best_layer2 = layer2; + best_layer1 = layer1.slice(); + best_layer2 = layer2.slice(); best_iter = 0; for (layer = layer1, iter = 0; @@ -1049,8 +1043,8 @@ reorder.barycenter1 = function(graph, comp, max_iter) { crossings = count_crossings(graph, layer1, layer2); if (crossings < best_crossings) { best_crossings = crossings; - best_layer1 = layer1; - best_layer2 = layer2; + best_layer1 = layer1.slice(); + best_layer2 = layer2.slice(); best_iter = iter; max_iter = Math.max(max_iter, iter + 2); // we improved so go on } diff --git a/reorder.v1.min.js b/reorder.v1.min.js index 234b54c..fce0c21 100644 --- a/reorder.v1.min.js +++ b/reorder.v1.min.js @@ -1,2 +1,3 @@ -(function(exports){reorder={version:"0.0.5"};reorder.cmp_number_asc=function(a,b){return a-b};reorder.cmp_number=reorder.cmp_number_asc;reorder.cmp_number_desc=function(a,b){return b-a};reorder.flatten=function(a,b){return a.concat(b)};reorder.infinities=function(n){var i=-1,a=[];if(arguments.length===1)while(++i1)s+=v[i];return s};function isNum(a,b){return!(isNaN(a)||isNaN(b)||a==Infinity||b==Infinity)}reorder.distance={euclidean:function(a,b){var i=a.length,s=0,x;while(i-->0){if(isNum(a[i],b[i])){x=a[i]-b[i];s+=x*x}}return Math.sqrt(s)},manhattan:function(a,b){var i=a.length,s=0;while(i-->0){if(isNum(a[i],b[i])){s+=Math.abs(a[i]-b[i])}}return s},minkowski:function(p){return function(a,b){var i=a.length,s=0;while(i-->0){if(isNum(a[i],b[i])){s+=Math.pow(Math.abs(a[i]-b[i]),p)}}return Math.pow(s,1/p)}},chebyshev:function(a,b){var i=a.length,max=0,x;while(i-->0){if(isNum(a[i],b[i])){x=Math.abs(a[i]-b[i]);if(x>max)max=x}}return max},hamming:function(a,b){var i=a.length,d=0;while(i-->0){if(isNum(a[i],b[i])){if(a[i]!==b[i])d++}}return d},jaccard:function(a,b){var n=0,i=a.length,s=0;while(i-->0){if(isNum(a[i],b[i])){if(a[i]===b[i])s++;n++}}if(n==0)return 0;return s/n},braycurtis:function(a,b){var i=a.length,s0=0,s1=0,ai,bi;while(i-->0){ai=a[i];bi=b[i];if(isNum(ai,bi)){s0+=Math.abs(ai-bi);s1+=Math.abs(ai+bi)}}if(s1==0)return 0;return s0/s1}};reorder.range=function(start,stop,step){if(arguments.length<3){step=1;if(arguments.length<2){stop=start;start=0}}var range=[],i=start;if(step<0)for(;i>stop;i+=step)range.push(i);else for(;i=1)return reorder.graph_complete(n,directed);var nodes=graph_empty_nodes(n),links=[],v,w,i,lr,lp;w=-1;lp=Math.log(1-p);if(directed){for(v=0;v=n&&v=v&&v=0&&v!=c)dist[c]=dist[v]+1});return dist};reorder.all_pairs_distance_bfs=function(graph,comps){if(!comps)comps=[graph.nodes_indices()];var nodes=comps.reduce(reorder.flatten).sort(reorder.cmp_number),mat=Array(nodes.length),i,j,dist;for(i=0;imax_value)max_value=v[j];links.push({source:i,target:j,value:v[j]})}}}return reorder.graph(nodes,links,directed).linkDistance(function(l,i){return 1+max_value-l.value}).init()};reorder.graph2mat=function(graph,directed){var nodes=graph.nodes(),links=graph.links(),n=nodes.length,i,l,mat;if(!directed)directed=graph.directed();if(directed){var rows=n,cols=n;for(i=n-1;i>=0;i--){if(graph.inEdges(i).length!=0)break;else rows--}for(i=n-1;i>=0;i--){if(graph.outEdges(i).length!=0)break;else cols--}mat=reorder.zeroes(rows,cols);for(i=0;i0){if(index%2)crosscount+=tree[index+1];index=index-1>>1;tree[index]++}}return crosscount}reorder.count_crossings=count_crossings;function count_in_crossings(graph,v,w,inv){var v_edges=graph.inEdges(v),w_edges=graph.inEdges(w),iv,iw,p0,cross=0;for(iw=0;iwp0)cross++}}return cross}function count_out_crossings(graph,v,w,inv){var v_edges=graph.outEdges(v),w_edges=graph.outEdges(w),iv,iw,p0,cross=0;for(iw=0;iwp0)cross++}}return cross}function adjacent_exchange(graph,layer1,layer2,crossings){var i,v,w,c0,c1,inv_layer1=inverse_permutation(layer1),inv_layer2=inverse_permutation(layer2),swapped=true;while(swapped){swapped=false;for(i=0;i0)return 1;return 0});for(i=0;idist[i][k]+dist[k][j]){dist[i][j]=dist[i][k]+dist[k][j];dist[j][i]=dist[i][j]}}}return dist}reorder.all_pairs_distance1=all_pairs_distance_floyd_warshall;function floyd_warshall_with_path(graph,comp){if(!comp)comp=graph.components()[0];var dist=reorder.infinities(comp.length,comp.length),next=Array(comp.length),directed=graph.directed(),edges,i,j,k,inv;inv=inverse_permutation(comp);for(i=0;idist[i][k]+dist[k][j]){dist[i][j]=dist[i][k]+dist[k][j];next[i][j]=next[i][k];if(!directed){dist[j][i]=dist[i][j];next[j][i]=next[k][j]}}}}}return[dist,next]}reorder.floyd_warshall_with_path=floyd_warshall_with_path;reorder.floyd_warshall_path=function(next,u,v){if(next[u][v]==undefined)return[];var path=[u];while(u!=v){u=next[u][v];path.push(u)}return path};function distmat2valuemat(distmat){var n=distmat.length,valuemat=reorder.zeroes(n,n),max_dist=reorder.distmax(distmat),i,j;for(i=0;imax)max=row[j]}return max};reorder.distmin=function(distMatrix){var min=Infinity,n=distMatrix.length,i,j,row;for(i=0;i0;)dist[i].splice(n,m-n);return dist};reorder.randomPermute=function(array,i,j){if(arguments.length<3){j=array.length;if(arguments.length<2){i=0}}var m=j-i,t,k;while(m>0){k=i+Math.floor(Math.random()*m--);t=array[i+m];array[i+m]=array[k];array[k]=t}return array};reorder.randomPermutation=function(n){return reorder.randomPermute(reorder.permutation(n))};reorder.random_matrix=function(p,n,m,sym){if(!m)m=n;if(n!=m)sym=false;else if(!sym)sym=true;var mat=reorder.zeroes(n,m),i,j,cnt;if(sym){for(i=0;i0)array[m]=reorder.permute(array[m],indexes);return array};reorder.stablepermute=function(list,indexes){var p=reorder.permute(list,indexes);if(p[0]>p[p.length-1])p.reverse();return p};if(typeof science=="undefined"){science={version:"1.9.1"};science.stats={}}science.stats.hcluster=function(){var distance=reorder.distance.euclidean,linkage="single",distMatrix=null;function hcluster(vectors){var n=vectors.length,dMin=[],cSize=[],clusters=[],c1,c2,c1Cluster,c2Cluster,p,root,i,j,id=0;if(distMatrix==null){distMatrix=[];i=-1;while(++idistMatrix[i][j])dMin[i]=j}}}else{if(distMatrix.lengthdistMatrix[i][j])dMin[i]=j}}}i=-1;while(++idistMatrix[c2][j])distMatrix[j][c1]=distMatrix[c1][j]=distMatrix[c2][j];break;case"complete":if(distMatrix[c1][j]0?i-1:0,j0=j0;k-=2){low=except[k-1];high=except[k];if(high>=j0){if(j0>j){j0=Math.min(j0,low+1);except.splice(k-1,2)}else{high=j0}}else if(low<=i0){if(i0j){if(perm[perm.length-1]!=perm.length-1)perm=perm.reverse();reorder.assert(perm[perm.length-1]==perm.length-1,"Invalid constrained permutation end")}if(i0!=0){perm=reorder.permutation(i0).concat(perm.map(function(v){return v+i0}))}if(orig.length>j0){perm=perm.concat(reorder.range(j0,orig.length))}return perm}function _order_except(){var perm,k,l,low,high,pos;if(except.length==0)return _order_equiv();_compute_dist();for(k=except.length-1;k>0;k-=2){low=except[k-1];high=except[k];distanceMatrix=reorder.dist_remove(distanceMatrix,low+1,high-1);vector.splice(low+1,high-low-2);if(debug)console.log("Except["+low+", "+high+"]");if(distanceMatrix[low][low+1]!=0){distanceMatrix[low][low+1]=distanceMatrix[low+1][low]=-1}}perm=_order_equiv();for(k=0;klow)perm[l]+=high-low-2;else if(perm[l]==low)pos=l}if(pos>0&&perm[pos-1]==high-1){Array.prototype.splice.apply(perm,[pos,0].concat(reorder.range(high-2,low,-1)))}else if(perm[pos+1]==high-1){Array.prototype.splice.apply(perm,[pos+1,0].concat(reorder.range(low+1,high-1)))}else{throw"Range not respected"}}return perm}function _order_equiv(){var perm,row,e,j,k,l,m,n,has_1=false,equiv=[],fix_except={};_compute_dist();for(k=0;kk;){if(row[l]==0){j=distanceMatrix[l].indexOf(-1);if(j!=-1){fix_except[k]=[l,j];distanceMatrix[j][k]=row[j]=-1;has_1=true}e.unshift(l);distanceMatrix=reorder.dist_remove(distanceMatrix,l);vector.splice(l,1)}else if(row[l]<0)has_1=true}if(e.length!=0){e.unshift(k);equiv.push(e)}}if(has_1){for(k=0;k0;){e=equiv[k];l=perm.indexOf(e[0]);m=fix_except[e[0]];if(m&&m[0]==e[0]){l=_fix_exception(perm,l,m[0],m[1],0);m=undefined}for(n=1;n0&&perm[l-1]==next){_swap(perm,l,perm.indexOf(m));return l+1}else if(perm[l+len+1]==next){_swap(perm,l+len,perm.indexOf(m));return l}else throw"Index not found"}function _swap(perm,a,b){if(a==b)return;var c=perm[a];perm[a]=perm[b];perm[b]=c}function _order(){if(debug>1)reorder.printmat(distanceMatrix);if(debug>2)reorder.printmat(vector);var perm=ordering().debug(debug).linkage(linkage).distanceMatrix(distanceMatrix)(vector);if(debug)console.log("Permutation: "+perm);return perm}function _perm_insert(perm,i,nv){perm=perm.map(function(v){return v=b)throw"Invalid list, indices not sorted";return a-b});return order};function _orderExcept(vector,i,j){var distanceMatrix=reorder.dist().distance(distance)(vector);var row,k,l,rev=false,args,pos=-1;distanceMatrix[i][i+1]=0;distanceMatrix[i+1][i]=0;var perm=ordering().distanceMatrix(distanceMatrix)(vector);pos=perm.indexOf(i);for(k=0;ki)perm[k]+=j-i-2}if(pos!=0&&perm[pos-1]==j-1)rev=true;if(rev){perm.reverse();pos=perm.length-pos-1}args=[pos+1,0].concat(reorder.range(i+1,j-1));Array.prototype.splice.apply(perm,args);return perm}order.orderrowsexcept=order.orderexcept;return order};reorder.covariance=science.lin.dot;reorder.covariancetranspose=function(v,a,b){var n=v.length,cov=0,i;for(i=0;i0)v[i]/=norm;return v}reorder.poweriteration=function(v,eps){if(arguments.length<2)eps=1e-4;var n=v.length,b=Array(n),i,j,tmp=Array(n),norm,s=10;reorder.assert(n==v[0].length,"poweriteration needs a square matrix");for(i=0;i0){for(i=0;i1)s+=v[i];return s};function isNum(a,b){return!(isNaN(a)||isNaN(b)||a==Infinity||b==Infinity)}reorder.distance={euclidean:function(a,b){var i=a.length,s=0,x;while(i-->0){if(isNum(a[i],b[i])){x=a[i]-b[i];s+=x*x}}return Math.sqrt(s)},manhattan:function(a,b){var i=a.length,s=0;while(i-->0){if(isNum(a[i],b[i])){s+=Math.abs(a[i]-b[i])}}return s},minkowski:function(p){return function(a,b){var i=a.length,s=0;while(i-->0){if(isNum(a[i],b[i])){s+=Math.pow(Math.abs(a[i]-b[i]),p)}}return Math.pow(s,1/p)}},chebyshev:function(a,b){var i=a.length,max=0,x;while(i-->0){if(isNum(a[i],b[i])){x=Math.abs(a[i]-b[i]);if(x>max)max=x}}return max},hamming:function(a,b){var i=a.length,d=0;while(i-->0){if(isNum(a[i],b[i])){if(a[i]!==b[i])d++}}return d},jaccard:function(a,b){var n=0,i=a.length,s=0;while(i-->0){if(isNum(a[i],b[i])){if(a[i]===b[i])s++;n++}}if(n==0)return 0;return s/n},braycurtis:function(a,b){var i=a.length,s0=0,s1=0,ai,bi;while(i-->0){ai=a[i];bi=b[i];if(isNum(ai,bi)){s0+=Math.abs(ai-bi);s1+=Math.abs(ai+bi)}}if(s1==0)return 0;return s0/s1}};reorder.range=function(start,stop,step){if(arguments.length<3){step=1;if(arguments.length<2){stop=start;start=0}}var range=[],i=start;if(step<0)for(;i>stop;i+=step)range.push(i);else for(;i=1)return reorder.graph_complete(n,directed);var nodes=graph_empty_nodes(n),links=[],v,w,i,lr,lp;w=-1;lp=Math.log(1-p);if(directed){for(v=0;v=n&&v=v&&v=0&&v!=c)dist[c]=dist[v]+1});return dist};reorder.all_pairs_distance_bfs=function(graph,comps){if(!comps)comps=[graph.nodes_indices()];var nodes=comps.reduce(reorder.flatten).sort(reorder.cmp_number),mat=Array(nodes.length),i,j,dist;for(i=0;imax_value)max_value=v[j];links.push({source:i,target:j,value:v[j]})}}}return reorder.graph(nodes,links,directed).linkDistance(function(l,i){return 1+max_value-l.value}).init()};reorder.graph2mat=function(graph,directed){var nodes=graph.nodes(),links=graph.links(),n=nodes.length,i,l,mat;if(!directed)directed=graph.directed();if(directed){var rows=n,cols=n;for(i=n-1;i>=0;i--){if(graph.inEdges(i).length!=0)break;else rows--}for(i=n-1;i>=0;i--){if(graph.outEdges(i).length!=0)break;else cols--}mat=reorder.zeroes(rows,cols);for(i=0;i0){if(index%2)crosscount+=tree[index+1];index=index-1>>1;tree[index]++}}return crosscount}reorder.count_crossings=count_crossings;function count_in_crossings(graph,v,w,inv){var v_edges=graph.inEdges(v),w_edges=graph.inEdges(w),iv,iw,p0,cross=0;for(iw=0;iwp0)cross++}}return cross}function count_out_crossings(graph,v,w,inv){var v_edges=graph.outEdges(v),w_edges=graph.outEdges(w),iv,iw,p0,cross=0;for(iw=0;iwp0)cross++}}return cross}function adjacent_exchange(graph,layer1,layer2){layer1=layer1.slice();layer2=layer2.slice();var i,v,w,c0,c1,inv_layer1=inverse_permutation(layer1),inv_layer2=inverse_permutation(layer2),swapped=true,improved=0;while(swapped){swapped=false;for(i=0;ic1){layer1[i]=w;layer1[i+1]=v;inv_layer1[w]=i;inv_layer1[v]=i+1;swapped=true;improved+=c0-c1}}for(i=0;ic1){layer2[i]=w;layer2[i+1]=v;inv_layer2[w]=i;inv_layer2[v]=i+1;swapped=true;improved+=c0-c1}}}return[layer1,layer2,improved]}reorder.adjacent_exchange=adjacent_exchange;reorder.barycenter=function(graph,iter,comps){var perms=[[],[],0];if(!comps){comps=graph.components()}for(var i=0;i0)return 1;return 0});for(i=0;idist[i][k]+dist[k][j]){dist[i][j]=dist[i][k]+dist[k][j];dist[j][i]=dist[i][j]}}}return dist}reorder.all_pairs_distance1=all_pairs_distance_floyd_warshall;function floyd_warshall_with_path(graph,comp){if(!comp)comp=graph.components()[0];var dist=reorder.infinities(comp.length,comp.length),next=Array(comp.length),directed=graph.directed(),edges,i,j,k,inv;inv=inverse_permutation(comp);for(i=0;idist[i][k]+dist[k][j]){dist[i][j]=dist[i][k]+dist[k][j];next[i][j]=next[i][k];if(!directed){dist[j][i]=dist[i][j];next[j][i]=next[k][j]}}}}}return[dist,next]}reorder.floyd_warshall_with_path=floyd_warshall_with_path;reorder.floyd_warshall_path=function(next,u,v){if(next[u][v]==undefined)return[];var path=[u];while(u!=v){u=next[u][v];path.push(u)}return path};function distmat2valuemat(distmat){var n=distmat.length,valuemat=reorder.zeroes(n,n),max_dist=reorder.distmax(distmat),i,j;for(i=0;imax)max=row[j]}return max};reorder.distmin=function(distMatrix){var min=Infinity,n=distMatrix.length,i,j,row;for(i=0;i0;)dist[i].splice(n,m-n);return dist};reorder.randomPermute=function(array,i,j){if(arguments.length<3){j=array.length;if(arguments.length<2){i=0}}var m=j-i,t,k;while(m>0){k=i+Math.floor(Math.random()*m--);t=array[i+m];array[i+m]=array[k];array[k]=t}return array};reorder.randomPermutation=function(n){return reorder.randomPermute(reorder.permutation(n))};reorder.random_matrix=function(p,n,m,sym){if(!m)m=n;if(n!=m)sym=false;else if(!sym)sym=true;var mat=reorder.zeroes(n,m),i,j,cnt;if(sym){for(i=0;i0)array[m]=reorder.permute(array[m],indexes);return array};reorder.stablepermute=function(list,indexes){var p=reorder.permute(list,indexes);if(p[0]>p[p.length-1])p.reverse();return p};if(typeof science=="undefined"){science={version:"1.9.1"};science.stats={}}science.stats.hcluster=function(){var distance=reorder.distance.euclidean,linkage="single",distMatrix=null;function hcluster(vectors){var n=vectors.length,dMin=[],cSize=[],clusters=[],c1,c2,c1Cluster,c2Cluster,p,root,i,j,id=0;if(distMatrix==null){distMatrix=[];i=-1;while(++idistMatrix[i][j])dMin[i]=j}}}else{if(distMatrix.lengthdistMatrix[i][j])dMin[i]=j}}}i=-1;while(++idistMatrix[c2][j])distMatrix[j][c1]=distMatrix[c1][j]=distMatrix[c2][j];break;case"complete":if(distMatrix[c1][j]0?i-1:0,j0=j0;k-=2){low=except[k-1];high=except[k];if(high>=j0){if(j0>j){j0=Math.min(j0,low+1);except.splice(k-1,2)}else{high=j0}}else if(low<=i0){if(i0j){if(perm[perm.length-1]!=perm.length-1)perm=perm.reverse();reorder.assert(perm[perm.length-1]==perm.length-1,"Invalid constrained permutation end")}if(i0!=0){perm=reorder.permutation(i0).concat(perm.map(function(v){return v+i0}))}if(orig.length>j0){perm=perm.concat(reorder.range(j0,orig.length))}return perm}function _order_except(){var perm,k,l,low,high,pos;if(except.length==0)return _order_equiv();_compute_dist();for(k=except.length-1;k>0;k-=2){low=except[k-1];high=except[k];distanceMatrix=reorder.dist_remove(distanceMatrix,low+1,high-1);vector.splice(low+1,high-low-2);if(debug)console.log("Except["+low+", "+high+"]");if(distanceMatrix[low][low+1]!=0){distanceMatrix[low][low+1]=distanceMatrix[low+1][low]=-1}}perm=_order_equiv();for(k=0;klow)perm[l]+=high-low-2;else if(perm[l]==low)pos=l}if(pos>0&&perm[pos-1]==high-1){Array.prototype.splice.apply(perm,[pos,0].concat(reorder.range(high-2,low,-1)))}else if(perm[pos+1]==high-1){Array.prototype.splice.apply(perm,[pos+1,0].concat(reorder.range(low+1,high-1)))}else{throw"Range not respected"}}return perm}function _order_equiv(){var perm,row,e,j,k,l,m,n,has_1=false,equiv=[],fix_except={};_compute_dist();for(k=0;kk;){if(row[l]==0){j=distanceMatrix[l].indexOf(-1);if(j!=-1){fix_except[k]=[l,j];distanceMatrix[j][k]=row[j]=-1;has_1=true}e.unshift(l);distanceMatrix=reorder.dist_remove(distanceMatrix,l);vector.splice(l,1)}else if(row[l]<0)has_1=true}if(e.length!=0){e.unshift(k);equiv.push(e)}}if(has_1){for(k=0;k0;){e=equiv[k];l=perm.indexOf(e[0]);m=fix_except[e[0]];if(m&&m[0]==e[0]){l=_fix_exception(perm,l,m[0],m[1],0);m=undefined}for(n=1;n0&&perm[l-1]==next){_swap(perm,l,perm.indexOf(m));return l+1}else if(perm[l+len+1]==next){_swap(perm,l+len,perm.indexOf(m));return l}else throw"Index not found"}function _swap(perm,a,b){if(a==b)return;var c=perm[a];perm[a]=perm[b];perm[b]=c}function _order(){if(debug>1)reorder.printmat(distanceMatrix);if(debug>2)reorder.printmat(vector);var perm=ordering().debug(debug).linkage(linkage).distanceMatrix(distanceMatrix)(vector);if(debug)console.log("Permutation: "+perm);return perm}function _perm_insert(perm,i,nv){perm=perm.map(function(v){return v=b)throw"Invalid list, indices not sorted";return a-b});return order};function _orderExcept(vector,i,j){var distanceMatrix=reorder.dist().distance(distance)(vector);var row,k,l,rev=false,args,pos=-1;distanceMatrix[i][i+1]=0;distanceMatrix[i+1][i]=0;var perm=ordering().distanceMatrix(distanceMatrix)(vector);pos=perm.indexOf(i);for(k=0;ki)perm[k]+=j-i-2}if(pos!=0&&perm[pos-1]==j-1)rev=true;if(rev){perm.reverse();pos=perm.length-pos-1}args=[pos+1,0].concat(reorder.range(i+1,j-1));Array.prototype.splice.apply(perm,args);return perm}order.orderrowsexcept=order.orderexcept;return order};reorder.covariance=science.lin.dot;reorder.covariancetranspose=function(v,a,b){var n=v.length,cov=0,i;for(i=0;i0)v[i]/=norm; + +return v}reorder.poweriteration=function(v,eps){if(arguments.length<2)eps=1e-4;var n=v.length,b=Array(n),i,j,tmp=Array(n),norm,s=10;reorder.assert(n==v[0].length,"poweriteration needs a square matrix");for(i=0;i0){for(i=0;i p0) + cross++; + } + } + return cross; +} + +function count_out_crossings(graph, v, w, inv) { + var v_edges = graph.outEdges(v), + w_edges = graph.outEdges(w), + iv, iw, p0, cross = 0; + + for (iw = 0; iw < w_edges.length; iw++) { + p0 = inv[w_edges[iw].source.index]; + for (iv = 0; iv < v_edges.length; iv++) { + if (inv[v_edges[iv].source.index] > p0) + cross++; + } + } + return cross; +} + +function adjacent_exchange(graph, layer1, layer2) { + layer1 = layer1.slice(); + layer2 = layer2.slice(); + var i, v, w, c0, c1, + inv_layer1 = inverse_permutation(layer1), + inv_layer2 = inverse_permutation(layer2), + swapped = true, + improved = 0; + + while (swapped) { + swapped = false; + for (i = 0; i < layer1.length-1; i++) { + v = layer1[i]; + w = layer1[i+1]; + // should reduce the in crossing and the out crossing + // otherwise what we gain horizontally is lost vertically + c0 = count_out_crossings(graph, v, w, inv_layer2); + c1 = count_out_crossings(graph, w, v, inv_layer2); + if (c0 > c1) { + layer1[i] = w; + layer1[i+1] = v; + inv_layer1[w] = i; + inv_layer1[v] = i+1; + swapped = true; + improved += c0 - c1; + } + } + for (i = 0; i < layer2.length-1; i++) { + v = layer2[i]; + w = layer2[i+1]; + c0 = count_in_crossings(graph, v, w, inv_layer1); + c1 = count_in_crossings(graph, w, v, inv_layer1); + if (c0 > c1) { + layer2[i] = w; + layer2[i+1] = v; + inv_layer2[w] = i; + inv_layer2[v] = i+1; + swapped = true; + improved += c0 - c1; + } + } + } + + return [layer1, layer2, improved]; +}; + +reorder.adjacent_exchange = adjacent_exchange; diff --git a/src/barycenter.js b/src/barycenter.js index 7e21519..828eacd 100644 --- a/src/barycenter.js +++ b/src/barycenter.js @@ -44,10 +44,10 @@ reorder.barycenter1 = function(graph, comp, max_iter) { i, v, neighbors; layer1 = comp.filter(function(n) { - return graph.outEdges(n).length!=0; + return graph.outDegree(n) != 0; }); layer2 = comp.filter(function(n) { - return graph.inEdges(n).length!=0; + return graph.inDegree(n) != 0; }); if (comp.length < 3) { return [layer1, layer2, @@ -59,19 +59,12 @@ reorder.barycenter1 = function(graph, comp, max_iter) { else if ((max_iter%2)==1) max_iter++; // want even number of iterations - layer1 = comp.filter(function(n) { - return graph.outEdges(n).length!=0; - }); - layer2 = comp.filter(function(n) { - return graph.inEdges(n).length!=0; - }); - for (i = 0; i < layer2.length; i++) nodes[layer2[i]].pos = i; best_crossings = count_crossings(graph, layer1, layer2); - best_layer1 = layer1; - best_layer2 = layer2; + best_layer1 = layer1.slice(); + best_layer2 = layer2.slice(); best_iter = 0; for (layer = layer1, iter = 0; @@ -109,8 +102,8 @@ reorder.barycenter1 = function(graph, comp, max_iter) { crossings = count_crossings(graph, layer1, layer2); if (crossings < best_crossings) { best_crossings = crossings; - best_layer1 = layer1; - best_layer2 = layer2; + best_layer1 = layer1.slice(); + best_layer2 = layer2.slice(); best_iter = iter; max_iter = Math.max(max_iter, iter + 2); // we improved so go on } diff --git a/src/count_crossings.js b/src/count_crossings.js index 0297597..1cfa302 100644 --- a/src/count_crossings.js +++ b/src/count_crossings.js @@ -6,13 +6,14 @@ function count_crossings(graph, north, south) { firstIndex, treeSize, tree, index, weightSum, invert = false, crosscount; + var comp = reorder.permutation(graph.nodes().length); + if (north==undefined) { - var comp = reorder.permutation(graph.nodes().length); north = comp.filter(function(n) { - return graph.outEdges(n).length!=0; + return graph.outDegree(n) != 0; }), south = comp.filter(function(n) { - return graph.inEdges(n).length!=0; + return graph.inDegree(n) != 0; }); } @@ -63,4 +64,5 @@ function count_crossings(graph, north, south) { } return crosscount; } + reorder.count_crossings = count_crossings; diff --git a/src/graph.js b/src/graph.js index 8434d32..692fc2c 100644 --- a/src/graph.js +++ b/src/graph.js @@ -139,8 +139,6 @@ reorder.graph = function(nodes, links, directed) { graph.sinks = function() { var sinks = [], i; - if (! directed) - return reorder.range(nodes.length); for (i = 0; i < nodes.length; i++) { if (outEdges(i).length == 0) @@ -152,8 +150,6 @@ reorder.graph = function(nodes, links, directed) { graph.sources = function() { var sources = [], i; - if (! directed) - return reorder.range(nodes.length); for (i = 0; i < nodes.length; i++) { if (inEdges(i).length == 0) diff --git a/test/barycenter-test.js b/test/barycenter-test.js index d35ce97..f7fe30f 100644 --- a/test/barycenter-test.js +++ b/test/barycenter-test.js @@ -9,17 +9,26 @@ var suite = vows.describe("reorder.barycenter"); function dotest(mat) { var graph = reorder.mat2graph(mat, true); - //reorder.printmat(mat); + //reorder.displaymat(mat); var initial_crossings = reorder.count_crossings(graph); var perms = reorder.barycenter(graph); - //console.log('VOrder: %j, HOrder: %j, Crossings: %d', - //perms[1], perms[0], perms[2]); - //reorder.printmat(mat, perms[1], perms[0]); + // console.log('VOrder: %j, HOrder: %j, Crossings: %d', + // perms[1], perms[0], perms[2]); + // reorder.displaymat(mat, perms[1], perms[0]); assert.isTrue(initial_crossings > perms[2]); - var perm2 = reorder.adjacent_exchange(graph, - perms[0], perms[1], perms[2]); - assert.isTrue(perm2[2] >= perms[2]); - console.log('Improved by: %d', perm2[2]-perms[2]); + var perms2 = reorder.adjacent_exchange(graph, perms[0], perms[1]); + if (perms2[2]) { + //reorder.displaymat(mat, perms2[1], perms2[0]); + var crossings = reorder.count_crossings( + graph, perms2[0], perms2[1]); + assert.equal(crossings, perms[2]-perms2[2]); + // console.log('final crossings: %d, improved by %d (%d%) %d', + // crossings, perms[2]-crossings, + // Math.round((perms[2]-crossings)*100.0/perms[2]), + // perms2[2]); + // console.log('VOrder: %j, HOrder: %j', perms2[1], perms2[0]); + assert.isTrue(crossings < perms[2]); + } } suite.addBatch({