StackGenVis: Alignment of Data, Algorithms, and Models for Stacking Ensemble Learning Using Performance Metrics https://doi.org/10.1109/TVCG.2020.3030352
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
StackGenVis/frontend/node_modules/simplicial-complex/topology.js

342 lines
7.8 KiB

"use strict"; "use restrict";
var bits = require("bit-twiddle")
, UnionFind = require("union-find")
//Returns the dimension of a cell complex
function dimension(cells) {
var d = 0
, max = Math.max
for(var i=0, il=cells.length; i<il; ++i) {
d = max(d, cells[i].length)
}
return d-1
}
exports.dimension = dimension
//Counts the number of vertices in faces
function countVertices(cells) {
var vc = -1
, max = Math.max
for(var i=0, il=cells.length; i<il; ++i) {
var c = cells[i]
for(var j=0, jl=c.length; j<jl; ++j) {
vc = max(vc, c[j])
}
}
return vc+1
}
exports.countVertices = countVertices
//Returns a deep copy of cells
function cloneCells(cells) {
var ncells = new Array(cells.length)
for(var i=0, il=cells.length; i<il; ++i) {
ncells[i] = cells[i].slice(0)
}
return ncells
}
exports.cloneCells = cloneCells
//Ranks a pair of cells up to permutation
function compareCells(a, b) {
var n = a.length
, t = a.length - b.length
, min = Math.min
if(t) {
return t
}
switch(n) {
case 0:
return 0;
case 1:
return a[0] - b[0];
case 2:
var d = a[0]+a[1]-b[0]-b[1]
if(d) {
return d
}
return min(a[0],a[1]) - min(b[0],b[1])
case 3:
var l1 = a[0]+a[1]
, m1 = b[0]+b[1]
d = l1+a[2] - (m1+b[2])
if(d) {
return d
}
var l0 = min(a[0], a[1])
, m0 = min(b[0], b[1])
, d = min(l0, a[2]) - min(m0, b[2])
if(d) {
return d
}
return min(l0+a[2], l1) - min(m0+b[2], m1)
//TODO: Maybe optimize n=4 as well?
default:
var as = a.slice(0)
as.sort()
var bs = b.slice(0)
bs.sort()
for(var i=0; i<n; ++i) {
t = as[i] - bs[i]
if(t) {
return t
}
}
return 0
}
}
exports.compareCells = compareCells
function compareZipped(a, b) {
return compareCells(a[0], b[0])
}
//Puts a cell complex into normal order for the purposes of findCell queries
function normalize(cells, attr) {
if(attr) {
var len = cells.length
var zipped = new Array(len)
for(var i=0; i<len; ++i) {
zipped[i] = [cells[i], attr[i]]
}
zipped.sort(compareZipped)
for(var i=0; i<len; ++i) {
cells[i] = zipped[i][0]
attr[i] = zipped[i][1]
}
return cells
} else {
cells.sort(compareCells)
return cells
}
}
exports.normalize = normalize
//Removes all duplicate cells in the complex
function unique(cells) {
if(cells.length === 0) {
return []
}
var ptr = 1
, len = cells.length
for(var i=1; i<len; ++i) {
var a = cells[i]
if(compareCells(a, cells[i-1])) {
if(i === ptr) {
ptr++
continue
}
cells[ptr++] = a
}
}
cells.length = ptr
return cells
}
exports.unique = unique;
//Finds a cell in a normalized cell complex
function findCell(cells, c) {
var lo = 0
, hi = cells.length-1
, r = -1
while (lo <= hi) {
var mid = (lo + hi) >> 1
, s = compareCells(cells[mid], c)
if(s <= 0) {
if(s === 0) {
r = mid
}
lo = mid + 1
} else if(s > 0) {
hi = mid - 1
}
}
return r
}
exports.findCell = findCell;
//Builds an index for an n-cell. This is more general than dual, but less efficient
function incidence(from_cells, to_cells) {
var index = new Array(from_cells.length)
for(var i=0, il=index.length; i<il; ++i) {
index[i] = []
}
var b = []
for(var i=0, n=to_cells.length; i<n; ++i) {
var c = to_cells[i]
var cl = c.length
for(var k=1, kn=(1<<cl); k<kn; ++k) {
b.length = bits.popCount(k)
var l = 0
for(var j=0; j<cl; ++j) {
if(k & (1<<j)) {
b[l++] = c[j]
}
}
var idx=findCell(from_cells, b)
if(idx < 0) {
continue
}
while(true) {
index[idx++].push(i)
if(idx >= from_cells.length || compareCells(from_cells[idx], b) !== 0) {
break
}
}
}
}
return index
}
exports.incidence = incidence
//Computes the dual of the mesh. This is basically an optimized version of buildIndex for the situation where from_cells is just the list of vertices
function dual(cells, vertex_count) {
if(!vertex_count) {
return incidence(unique(skeleton(cells, 0)), cells, 0)
}
var res = new Array(vertex_count)
for(var i=0; i<vertex_count; ++i) {
res[i] = []
}
for(var i=0, len=cells.length; i<len; ++i) {
var c = cells[i]
for(var j=0, cl=c.length; j<cl; ++j) {
res[c[j]].push(i)
}
}
return res
}
exports.dual = dual
//Enumerates all cells in the complex
function explode(cells) {
var result = []
for(var i=0, il=cells.length; i<il; ++i) {
var c = cells[i]
, cl = c.length|0
for(var j=1, jl=(1<<cl); j<jl; ++j) {
var b = []
for(var k=0; k<cl; ++k) {
if((j >>> k) & 1) {
b.push(c[k])
}
}
result.push(b)
}
}
return normalize(result)
}
exports.explode = explode
//Enumerates all of the n-cells of a cell complex
function skeleton(cells, n) {
if(n < 0) {
return []
}
var result = []
, k0 = (1<<(n+1))-1
for(var i=0; i<cells.length; ++i) {
var c = cells[i]
for(var k=k0; k<(1<<c.length); k=bits.nextCombination(k)) {
var b = new Array(n+1)
, l = 0
for(var j=0; j<c.length; ++j) {
if(k & (1<<j)) {
b[l++] = c[j]
}
}
result.push(b)
}
}
return normalize(result)
}
exports.skeleton = skeleton;
//Computes the boundary of all cells, does not remove duplicates
function boundary(cells) {
var res = []
for(var i=0,il=cells.length; i<il; ++i) {
var c = cells[i]
for(var j=0,cl=c.length; j<cl; ++j) {
var b = new Array(c.length-1)
for(var k=0, l=0; k<cl; ++k) {
if(k !== j) {
b[l++] = c[k]
}
}
res.push(b)
}
}
return normalize(res)
}
exports.boundary = boundary;
//Computes connected components for a dense cell complex
function connectedComponents_dense(cells, vertex_count) {
var labels = new UnionFind(vertex_count)
for(var i=0; i<cells.length; ++i) {
var c = cells[i]
for(var j=0; j<c.length; ++j) {
for(var k=j+1; k<c.length; ++k) {
labels.link(c[j], c[k])
}
}
}
var components = []
, component_labels = labels.ranks
for(var i=0; i<component_labels.length; ++i) {
component_labels[i] = -1
}
for(var i=0; i<cells.length; ++i) {
var l = labels.find(cells[i][0])
if(component_labels[l] < 0) {
component_labels[l] = components.length
components.push([cells[i].slice(0)])
} else {
components[component_labels[l]].push(cells[i].slice(0))
}
}
return components
}
//Computes connected components for a sparse graph
function connectedComponents_sparse(cells) {
var vertices = unique(normalize(skeleton(cells, 0)))
, labels = new UnionFind(vertices.length)
for(var i=0; i<cells.length; ++i) {
var c = cells[i]
for(var j=0; j<c.length; ++j) {
var vj = findCell(vertices, [c[j]])
for(var k=j+1; k<c.length; ++k) {
labels.link(vj, findCell(vertices, [c[k]]))
}
}
}
var components = []
, component_labels = labels.ranks
for(var i=0; i<component_labels.length; ++i) {
component_labels[i] = -1
}
for(var i=0; i<cells.length; ++i) {
var l = labels.find(findCell(vertices, [cells[i][0]]));
if(component_labels[l] < 0) {
component_labels[l] = components.length
components.push([cells[i].slice(0)])
} else {
components[component_labels[l]].push(cells[i].slice(0))
}
}
return components
}
//Computes connected components for a cell complex
function connectedComponents(cells, vertex_count) {
if(vertex_count) {
return connectedComponents_dense(cells, vertex_count)
}
return connectedComponents_sparse(cells)
}
exports.connectedComponents = connectedComponents