From 9d92de5621f7ae3ec517d409e1012b411a94c655 Mon Sep 17 00:00:00 2001 From: Jean-Daniel Fekete Date: Sat, 18 Apr 2015 08:29:24 -0400 Subject: [PATCH] barycenter returns the configuration with lowest crossings --- Makefile | 3 +- reorder.v1.js | 36 ++++++--- reorder.v1.min.js | 2 +- src/barycenter.js | 36 ++++++--- src/count_crossings.js | 56 ++++++++++++++ src/package.js | 3 + test/barycenter-test.js | 2 + test/count_crossings-test.js | 142 +++++++++++++++++++++++++++++++++++ 8 files changed, 255 insertions(+), 25 deletions(-) create mode 100644 src/count_crossings.js create mode 100644 test/count_crossings-test.js diff --git a/Makefile b/Makefile index 69a948d..555cb83 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,5 @@ NODE_PATH = ./node_modules JS_COMPILER = $(NODE_PATH)/uglify-js/bin/uglifyjs -JS_TESTER = $(NODE_PATH)/vows/bin/vows --nocolor -v all: \ reorder.v1.js \ @@ -40,7 +39,7 @@ reorder.v1.js: \ src/ca.js test: all - @$(JS_TESTER) + @npm test %.min.js: %.js Makefile @rm -f $@ diff --git a/reorder.v1.js b/reorder.v1.js index 0af19ef..c74be39 100644 --- a/reorder.v1.js +++ b/reorder.v1.js @@ -726,19 +726,20 @@ function median(neighbors) { return (neighbors[lm]*rspan + neighbors[rm]*lspan) / (lspan+rspan); } -reorder.barycenter1 = function(graph, comp, iter) { +reorder.barycenter1 = function(graph, comp, max_iter) { var nodes = graph.nodes(), - layer1, layer2, - layer, + layer1, layer2, crossings, iter, + best_layer1, best_layer2, best_crossings, best_iter, + layer, i, v, neighbors; if (comp.length < 3) return comp; - if (! iter) - iter = 24; - else if ((iter%2)==1) - iter++; // want even number of iterations + if (! max_iter) + max_iter = 24; + else if ((max_iter%2)==1) + max_iter++; // want even number of iterations layer1 = comp.filter(function(n) { return graph.outEdges(n).length!=0; @@ -750,9 +751,14 @@ reorder.barycenter1 = function(graph, comp, iter) { for (i = 0; i < layer2.length; i++) nodes[layer2[i]].pos = i; - for (layer = layer1; - iter--; - layer = (layer == layer1) ? layer2 : layer1) { + best_crossings = count_crossings(graph, layer1, layer2); + best_layer1 = layer1; + best_layer2 = layer2; + best_iter = 0; + + for (layer = layer1, iter = 0; + iter < max_iter; + iter++, layer = (layer == layer1) ? layer2 : layer1) { for (i = 0; i < layer.length; i++) { // Compute the median/barycenter for this node and set // its (real) value into node.mval @@ -782,8 +788,16 @@ reorder.barycenter1 = function(graph, comp, iter) { }); for (i = 0; i < layer2.length; i++) nodes[layer[i]].pos = i; + crossings = count_crossings(graph, layer1, layer2); + if (crossings < best_crossings) { + best_crossings = crossings; + best_layer1 = layer1; + best_layer2 = layer2; + best_iter = iter; + } } - return [layer1, layer2]; + console.log('Best iter: '+best_iter); + return [best_layer1, best_layer2, best_crossings]; }; reorder.dijkstra = function(graph) { var g = graph, dijkstra = {}; diff --git a/reorder.v1.min.js b/reorder.v1.min.js index c3992ef..71c200b 100644 --- a/reorder.v1.min.js +++ b/reorder.v1.min.js @@ -1 +1 @@ -(function(exports){reorder={version:"0.0.2"};reorder.dot=science.lin.dot;reorder.length=science.lin.length;reorder.normalize=science.lin.normalize;reorder.printmat=function(m){var i,j,row,line;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(;i1)percolateDown(0);delete index[top];return top};function lChild(i){return i*2+1}function rChild(i){return i*2+2}function parent(i){return Math.floor((i-1)/2)}function swap(i,j){var io=heap[i],jo=heap[j];heap[i]=jo;index[jo]=i;heap[j]=io;index[io]=j}function update(o){var i=percolateUp(index[o],o);percolateDown(i)}function percolateDown(cur){while(true){var left=lChild(cur),right=rChild(cur);var smallest;if(left0&&comp(heap[par],o)>0;par=parent(par)){var p=heap[par];heap[i]=p;index[p]=i;i=par}heap[i]=o;index[o]=i;return i}return Heap};reorder.permutation=reorder.range;function inverse_permutation(perm){var inv={};for(var i=0;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=science.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;reorder.barycenter=function(graph,iter,comps){var perm=[];if(!comps){comps=graph.components()}for(var i=0;i0)return 1;return 0});for(i=0;id){D.weight=d;D.edge=e;queue.update(D.vertex)}}}return queued}dijkstra.shortestPath=function(from,to){var map=allShortestPaths(from),path,v;v=map[to];path=[v];while(v.edge!=-1){v=map[graph.other(v.edge,v.vertex).index];path.unshift(v)}return path};return dijkstra};reorder.distmax=function(distMatrix){var max=0,n=distMatrix.length,i,j,row;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.randomMatrix=function(p,n,m,sym){if(!m)m=n;if(n!=m)sym=false;else if(!sym)sym=true;var mat=science.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(;i1)percolateDown(0);delete index[top];return top};function lChild(i){return i*2+1}function rChild(i){return i*2+2}function parent(i){return Math.floor((i-1)/2)}function swap(i,j){var io=heap[i],jo=heap[j];heap[i]=jo;index[jo]=i;heap[j]=io;index[io]=j}function update(o){var i=percolateUp(index[o],o);percolateDown(i)}function percolateDown(cur){while(true){var left=lChild(cur),right=rChild(cur);var smallest;if(left0&&comp(heap[par],o)>0;par=parent(par)){var p=heap[par];heap[i]=p;index[p]=i;i=par}heap[i]=o;index[o]=i;return i}return Heap};reorder.permutation=reorder.range;function inverse_permutation(perm){var inv={};for(var i=0;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=science.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;reorder.barycenter=function(graph,iter,comps){var perm=[];if(!comps){comps=graph.components()}for(var i=0;i0)return 1;return 0});for(i=0;id){D.weight=d;D.edge=e;queue.update(D.vertex)}}}return queued}dijkstra.shortestPath=function(from,to){var map=allShortestPaths(from),path,v;v=map[to];path=[v];while(v.edge!=-1){v=map[graph.other(v.edge,v.vertex).index];path.unshift(v)}return path};return dijkstra};reorder.distmax=function(distMatrix){var max=0,n=distMatrix.length,i,j,row;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.randomMatrix=function(p,n,m,sym){if(!m)m=n;if(n!=m)sym=false;else if(!sym)sym=true;var mat=science.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 0) { + if (index%2) crosscount += tree[index+1]; + index = (index - 1) >> 1; + tree[index]++; + } + } + return crosscount; +} +reorder.count_crossings = count_crossings; diff --git a/src/package.js b/src/package.js index 705916f..4fe9b8d 100644 --- a/src/package.js +++ b/src/package.js @@ -23,6 +23,9 @@ require("util").puts(JSON.stringify({ "vows": "0.8.1", "jsonfile": "2.0.0" }, + "scripts": { + "test": "vows; echo" + }, "licenses": [ { "type": "BSD-3", diff --git a/test/barycenter-test.js b/test/barycenter-test.js index 394a185..65c001a 100644 --- a/test/barycenter-test.js +++ b/test/barycenter-test.js @@ -19,6 +19,8 @@ suite.addBatch({ expect = [0, 1, 3, 2, 4]; reorder.printmat(mat); perm = reorder.barycenter(graph); + console.log('VOrder: %j, HOrder: %j, Crossings: %d', + perm[1], perm[0], perm[2]); reorder.printmat(mat, perm[1], perm[0]); //assert.deepEqual(perm, expect); } diff --git a/test/count_crossings-test.js b/test/count_crossings-test.js new file mode 100644 index 0000000..a38666b --- /dev/null +++ b/test/count_crossings-test.js @@ -0,0 +1,142 @@ +require("science"); +require("../reorder.v1"); +require("../reorder.v1"); + +var vows = require("vows"), + assert = require("assert"), + seedrandom = require('seedrandom'); +//var jf = require('jsonfile'); + +Math.seedrandom('reorder'); + +var suite = vows.describe("reorder.count_crossings"); + +function naive_count_crossings(graph, north, south) { + var i, j, e1, e2, v, count = 0, + inv_north = reorder.inverse_permutation(north), + inv_south = reorder.inverse_permutation(south), + links = []; + for (i = 0; i < north.length; i++) { + v = north[i]; + links = links.concat(graph.outEdges(v).map(function(e) { + return [ inv_north[e.target.index], inv_south[e.source.index] ]; + })); + } + for (i = 0; i < links.length; i++) { + e1 = links[i]; + for (j = i+1; j < links.length; j++) { + e2 = links[j]; + if ((e1[0] < e2[0] && e1[1] > e2[1]) + || (e1[0] > e2[0] && e1[1] < e2[1])) + count++; + } + } + return count; +} + +function dohard(mat) { + var graph = reorder.mat2graph(mat, true), + comps = graph.components(), + comp = comps.reduce(function(a, b) { + return (a.length > b.length) ? a : b; + }); + comp.sort(function(a,b){return a-b; }); + var layer1 = comp.filter(function(n) { + return graph.outEdges(n).length!=0; + }), + layer2 = comp.filter(function(n) { + return graph.inEdges(n).length!=0; + }); + //console.time('fast_crossings'); + var c1 = reorder.count_crossings(graph, layer1, layer2); + //console.timeEnd('fast_crossings'); + //console.time('naive_crossings'); + var c2 = naive_count_crossings(graph, layer1, layer2); + //console.timeEnd('naive_crossings'); + // if (c1 != c2) { + // var file = 'error_count_crossings.json'; + // jf.writeFile(file, mat, function(err) { + // console.log(err); + // }); + // } + assert.equal(c1, c2); +} + +suite.addBatch({ + "count_crossings": { + "simple": function() { + var graph = reorder.graph() + .nodes([{id: 0}, {id: 1}, {id: 2}, {id: 3}, {id: 4}, {id: 5}]) + .links([{source: 0, target: 0}, + {source: 1, target: 1}, + {source: 1, target: 2}, + {source: 2, target: 0}, + {source: 2, target: 3}, + {source: 2, target: 4}, + {source: 3, target: 0}, + {source: 3, target: 2}, + {source: 4, target: 3}, + {source: 5, target: 2}, + {source: 5, target: 4}]) + .directed(true) + .init(), + comp = graph.components()[0], + layer1 = comp.filter(function(n) { + return graph.outEdges(n).length!=0; + }), + layer2 = comp.filter(function(n) { + return graph.inEdges(n).length!=0; + }); + + + assert.equal(naive_count_crossings(graph, layer1, layer2), + 12); + assert.equal(reorder.count_crossings(graph, layer1, layer2), + 12); + }, + "bug": function() { + dohard([ + [0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0], + [0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], + [1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0], + [1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0], + [0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1], + [0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1], + [0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0], + [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1], + [0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], + [0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0], + [0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1], + [1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0], + [1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1], + [0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0], + [1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0], + [0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0] + ]); + }, + "hard": function() { + for (var i = 10; i < 100; i += 20) { + for (var j = 10; j < 100; j += 20) { + var mat = reorder.randomMatrix(0.2, i, j, false); + dohard(mat); + } + } + } + } +}); + +suite.export(module);