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.
415 lines
10 KiB
415 lines
10 KiB
"use strict"
|
|
|
|
var pool = require("typedarray-pool")
|
|
|
|
module.exports = createSurfaceExtractor
|
|
|
|
//Helper macros
|
|
function array(i) {
|
|
return "a" + i
|
|
}
|
|
function data(i) {
|
|
return "d" + i
|
|
}
|
|
function cube(i,bitmask) {
|
|
return "c" + i + "_" + bitmask
|
|
}
|
|
function shape(i) {
|
|
return "s" + i
|
|
}
|
|
function stride(i,j) {
|
|
return "t" + i + "_" + j
|
|
}
|
|
function offset(i) {
|
|
return "o" + i
|
|
}
|
|
function scalar(i) {
|
|
return "x" + i
|
|
}
|
|
function pointer(i) {
|
|
return "p" + i
|
|
}
|
|
function delta(i,bitmask) {
|
|
return "d" + i + "_" + bitmask
|
|
}
|
|
function index(i) {
|
|
return "i" + i
|
|
}
|
|
function step(i,j) {
|
|
return "u" + i + "_" + j
|
|
}
|
|
function pcube(bitmask) {
|
|
return "b" + bitmask
|
|
}
|
|
function qcube(bitmask) {
|
|
return "y" + bitmask
|
|
}
|
|
function pdelta(bitmask) {
|
|
return "e" + bitmask
|
|
}
|
|
function vert(i) {
|
|
return "v" + i
|
|
}
|
|
var VERTEX_IDS = "V"
|
|
var PHASES = "P"
|
|
var VERTEX_COUNT = "N"
|
|
var POOL_SIZE = "Q"
|
|
var POINTER = "X"
|
|
var TEMPORARY = "T"
|
|
|
|
function permBitmask(dimension, mask, order) {
|
|
var r = 0
|
|
for(var i=0; i<dimension; ++i) {
|
|
if(mask & (1<<i)) {
|
|
r |= (1<<order[i])
|
|
}
|
|
}
|
|
return r
|
|
}
|
|
|
|
//Generates the surface procedure
|
|
function compileSurfaceProcedure(vertexFunc, faceFunc, phaseFunc, scalarArgs, order, typesig) {
|
|
var arrayArgs = typesig.length
|
|
var dimension = order.length
|
|
|
|
if(dimension < 2) {
|
|
throw new Error("ndarray-extract-contour: Dimension must be at least 2")
|
|
}
|
|
|
|
var funcName = "extractContour" + order.join("_")
|
|
var code = []
|
|
var vars = []
|
|
var args = []
|
|
|
|
//Assemble arguments
|
|
for(var i=0; i<arrayArgs; ++i) {
|
|
args.push(array(i))
|
|
}
|
|
for(var i=0; i<scalarArgs; ++i) {
|
|
args.push(scalar(i))
|
|
}
|
|
|
|
//Shape
|
|
for(var i=0; i<dimension; ++i) {
|
|
vars.push(shape(i) + "=" + array(0) + ".shape[" + i + "]|0")
|
|
}
|
|
//Data, stride, offset pointers
|
|
for(var i=0; i<arrayArgs; ++i) {
|
|
vars.push(data(i) + "=" + array(i) + ".data",
|
|
offset(i) + "=" + array(i) + ".offset|0")
|
|
for(var j=0; j<dimension; ++j) {
|
|
vars.push(stride(i,j) + "=" + array(i) + ".stride[" + j + "]|0")
|
|
}
|
|
}
|
|
//Pointer, delta and cube variables
|
|
for(var i=0; i<arrayArgs; ++i) {
|
|
vars.push(pointer(i) + "=" + offset(i))
|
|
vars.push(cube(i,0))
|
|
for(var j=1; j<(1<<dimension); ++j) {
|
|
var ptrStr = []
|
|
for(var k=0; k<dimension; ++k) {
|
|
if(j & (1<<k)) {
|
|
ptrStr.push("-" + stride(i,k))
|
|
}
|
|
}
|
|
vars.push(delta(i,j) + "=(" + ptrStr.join("") + ")|0")
|
|
vars.push(cube(i,j) + "=0")
|
|
}
|
|
}
|
|
//Create step variables
|
|
for(var i=0; i<arrayArgs; ++i) {
|
|
for(var j=0; j<dimension; ++j) {
|
|
var stepVal = [ stride(i,order[j]) ]
|
|
if(j > 0) {
|
|
stepVal.push(stride(i, order[j-1]) + "*" + shape(order[j-1]) )
|
|
}
|
|
vars.push(step(i,order[j]) + "=(" + stepVal.join("-") + ")|0")
|
|
}
|
|
}
|
|
//Create index variables
|
|
for(var i=0; i<dimension; ++i) {
|
|
vars.push(index(i) + "=0")
|
|
}
|
|
//Vertex count
|
|
vars.push(VERTEX_COUNT + "=0")
|
|
//Compute pool size, initialize pool step
|
|
var sizeVariable = ["2"]
|
|
for(var i=dimension-2; i>=0; --i) {
|
|
sizeVariable.push(shape(order[i]))
|
|
}
|
|
//Previous phases and vertex_ids
|
|
vars.push(POOL_SIZE + "=(" + sizeVariable.join("*") + ")|0",
|
|
PHASES + "=mallocUint32(" + POOL_SIZE + ")",
|
|
VERTEX_IDS + "=mallocUint32(" + POOL_SIZE + ")",
|
|
POINTER + "=0")
|
|
//Create cube variables for phases
|
|
vars.push(pcube(0) + "=0")
|
|
for(var j=1; j<(1<<dimension); ++j) {
|
|
var cubeDelta = []
|
|
var cubeStep = [ ]
|
|
for(var k=0; k<dimension; ++k) {
|
|
if(j & (1<<k)) {
|
|
if(cubeStep.length === 0) {
|
|
cubeDelta.push("1")
|
|
} else {
|
|
cubeDelta.unshift(cubeStep.join("*"))
|
|
}
|
|
}
|
|
cubeStep.push(shape(order[k]))
|
|
}
|
|
var signFlag = ""
|
|
if(cubeDelta[0].indexOf(shape(order[dimension-2])) < 0) {
|
|
signFlag = "-"
|
|
}
|
|
var jperm = permBitmask(dimension, j, order)
|
|
vars.push(pdelta(jperm) + "=(-" + cubeDelta.join("-") + ")|0",
|
|
qcube(jperm) + "=(" + signFlag + cubeDelta.join("-") + ")|0",
|
|
pcube(jperm) + "=0")
|
|
}
|
|
vars.push(vert(0) + "=0", TEMPORARY + "=0")
|
|
|
|
function forLoopBegin(i, start) {
|
|
code.push("for(", index(order[i]), "=", start, ";",
|
|
index(order[i]), "<", shape(order[i]), ";",
|
|
"++", index(order[i]), "){")
|
|
}
|
|
|
|
function forLoopEnd(i) {
|
|
for(var j=0; j<arrayArgs; ++j) {
|
|
code.push(pointer(j), "+=", step(j,order[i]), ";")
|
|
}
|
|
code.push("}")
|
|
}
|
|
|
|
function fillEmptySlice(k) {
|
|
for(var i=k-1; i>=0; --i) {
|
|
forLoopBegin(i, 0)
|
|
}
|
|
var phaseFuncArgs = []
|
|
for(var i=0; i<arrayArgs; ++i) {
|
|
if(typesig[i]) {
|
|
phaseFuncArgs.push(data(i) + ".get(" + pointer(i) + ")")
|
|
} else {
|
|
phaseFuncArgs.push(data(i) + "[" + pointer(i) + "]")
|
|
}
|
|
}
|
|
for(var i=0; i<scalarArgs; ++i) {
|
|
phaseFuncArgs.push(scalar(i))
|
|
}
|
|
code.push(PHASES, "[", POINTER, "++]=phase(", phaseFuncArgs.join(), ");")
|
|
for(var i=0; i<k; ++i) {
|
|
forLoopEnd(i)
|
|
}
|
|
for(var j=0; j<arrayArgs; ++j) {
|
|
code.push(pointer(j), "+=", step(j,order[k]), ";")
|
|
}
|
|
}
|
|
|
|
function processGridCell(mask) {
|
|
//Read in local data
|
|
for(var i=0; i<arrayArgs; ++i) {
|
|
if(typesig[i]) {
|
|
code.push(cube(i,0), "=", data(i), ".get(", pointer(i), ");")
|
|
} else {
|
|
code.push(cube(i,0), "=", data(i), "[", pointer(i), "];")
|
|
}
|
|
}
|
|
|
|
//Read in phase
|
|
var phaseFuncArgs = []
|
|
for(var i=0; i<arrayArgs; ++i) {
|
|
phaseFuncArgs.push(cube(i,0))
|
|
}
|
|
for(var i=0; i<scalarArgs; ++i) {
|
|
phaseFuncArgs.push(scalar(i))
|
|
}
|
|
|
|
code.push(pcube(0), "=", PHASES, "[", POINTER, "]=phase(", phaseFuncArgs.join(), ");")
|
|
|
|
//Read in other cube data
|
|
for(var j=1; j<(1<<dimension); ++j) {
|
|
code.push(pcube(j), "=", PHASES, "[", POINTER, "+", pdelta(j), "];")
|
|
}
|
|
|
|
//Check for boundary crossing
|
|
var vertexPredicate = []
|
|
for(var j=1; j<(1<<dimension); ++j) {
|
|
vertexPredicate.push("(" + pcube(0) + "!==" + pcube(j) + ")")
|
|
}
|
|
code.push("if(", vertexPredicate.join("||"), "){")
|
|
|
|
//Read in boundary data
|
|
var vertexArgs = []
|
|
for(var i=0; i<dimension; ++i) {
|
|
vertexArgs.push(index(i))
|
|
}
|
|
for(var i=0; i<arrayArgs; ++i) {
|
|
vertexArgs.push(cube(i,0))
|
|
for(var j=1; j<(1<<dimension); ++j) {
|
|
if(typesig[i]) {
|
|
code.push(cube(i,j), "=", data(i), ".get(", pointer(i), "+", delta(i,j), ");")
|
|
} else {
|
|
code.push(cube(i,j), "=", data(i), "[", pointer(i), "+", delta(i,j), "];")
|
|
}
|
|
vertexArgs.push(cube(i,j))
|
|
}
|
|
}
|
|
for(var i=0; i<(1<<dimension); ++i) {
|
|
vertexArgs.push(pcube(i))
|
|
}
|
|
for(var i=0; i<scalarArgs; ++i) {
|
|
vertexArgs.push(scalar(i))
|
|
}
|
|
|
|
//Generate vertex
|
|
code.push("vertex(", vertexArgs.join(), ");",
|
|
vert(0), "=", VERTEX_IDS, "[", POINTER, "]=", VERTEX_COUNT, "++;")
|
|
|
|
//Check for face crossings
|
|
var base = (1<<dimension)-1
|
|
var corner = pcube(base)
|
|
for(var j=0; j<dimension; ++j) {
|
|
if((mask & ~(1<<j))===0) {
|
|
//Check face
|
|
var subset = base^(1<<j)
|
|
var edge = pcube(subset)
|
|
var faceArgs = [ ]
|
|
for(var k=subset; k>0; k=(k-1)&subset) {
|
|
faceArgs.push(VERTEX_IDS + "[" + POINTER + "+" + pdelta(k) + "]")
|
|
}
|
|
faceArgs.push(vert(0))
|
|
for(var k=0; k<arrayArgs; ++k) {
|
|
if(j&1) {
|
|
faceArgs.push(cube(k,base), cube(k,subset))
|
|
} else {
|
|
faceArgs.push(cube(k,subset), cube(k,base))
|
|
}
|
|
}
|
|
if(j&1) {
|
|
faceArgs.push(corner, edge)
|
|
} else {
|
|
faceArgs.push(edge, corner)
|
|
}
|
|
for(var k=0; k<scalarArgs; ++k) {
|
|
faceArgs.push(scalar(k))
|
|
}
|
|
code.push("if(", corner, "!==", edge, "){",
|
|
"face(", faceArgs.join(), ")}")
|
|
}
|
|
}
|
|
|
|
//Increment pointer, close off if statement
|
|
code.push("}",
|
|
POINTER, "+=1;")
|
|
}
|
|
|
|
function flip() {
|
|
for(var j=1; j<(1<<dimension); ++j) {
|
|
code.push(TEMPORARY, "=", pdelta(j), ";",
|
|
pdelta(j), "=", qcube(j), ";",
|
|
qcube(j), "=", TEMPORARY, ";")
|
|
}
|
|
}
|
|
|
|
function createLoop(i, mask) {
|
|
if(i < 0) {
|
|
processGridCell(mask)
|
|
return
|
|
}
|
|
fillEmptySlice(i)
|
|
code.push("if(", shape(order[i]), ">0){",
|
|
index(order[i]), "=1;")
|
|
createLoop(i-1, mask|(1<<order[i]))
|
|
|
|
for(var j=0; j<arrayArgs; ++j) {
|
|
code.push(pointer(j), "+=", step(j,order[i]), ";")
|
|
}
|
|
if(i === dimension-1) {
|
|
code.push(POINTER, "=0;")
|
|
flip()
|
|
}
|
|
forLoopBegin(i, 2)
|
|
createLoop(i-1, mask)
|
|
if(i === dimension-1) {
|
|
code.push("if(", index(order[dimension-1]), "&1){",
|
|
POINTER, "=0;}")
|
|
flip()
|
|
}
|
|
forLoopEnd(i)
|
|
code.push("}")
|
|
}
|
|
|
|
createLoop(dimension-1, 0)
|
|
|
|
//Release scratch memory
|
|
code.push("freeUint32(", VERTEX_IDS, ");freeUint32(", PHASES, ");")
|
|
|
|
//Compile and link procedure
|
|
var procedureCode = [
|
|
"'use strict';",
|
|
"function ", funcName, "(", args.join(), "){",
|
|
"var ", vars.join(), ";",
|
|
code.join(""),
|
|
"}",
|
|
"return ", funcName ].join("")
|
|
|
|
var proc = new Function(
|
|
"vertex",
|
|
"face",
|
|
"phase",
|
|
"mallocUint32",
|
|
"freeUint32",
|
|
procedureCode)
|
|
return proc(
|
|
vertexFunc,
|
|
faceFunc,
|
|
phaseFunc,
|
|
pool.mallocUint32,
|
|
pool.freeUint32)
|
|
}
|
|
|
|
function createSurfaceExtractor(args) {
|
|
function error(msg) {
|
|
throw new Error("ndarray-extract-contour: " + msg)
|
|
}
|
|
if(typeof args !== "object") {
|
|
error("Must specify arguments")
|
|
}
|
|
var order = args.order
|
|
if(!Array.isArray(order)) {
|
|
error("Must specify order")
|
|
}
|
|
var arrays = args.arrayArguments||1
|
|
if(arrays < 1) {
|
|
error("Must have at least one array argument")
|
|
}
|
|
var scalars = args.scalarArguments||0
|
|
if(scalars < 0) {
|
|
error("Scalar arg count must be > 0")
|
|
}
|
|
if(typeof args.vertex !== "function") {
|
|
error("Must specify vertex creation function")
|
|
}
|
|
if(typeof args.cell !== "function") {
|
|
error("Must specify cell creation function")
|
|
}
|
|
if(typeof args.phase !== "function") {
|
|
error("Must specify phase function")
|
|
}
|
|
var getters = args.getters || []
|
|
var typesig = new Array(arrays)
|
|
for(var i=0; i<arrays; ++i) {
|
|
if(getters.indexOf(i) >= 0) {
|
|
typesig[i] = true
|
|
} else {
|
|
typesig[i] = false
|
|
}
|
|
}
|
|
return compileSurfaceProcedure(
|
|
args.vertex,
|
|
args.cell,
|
|
args.phase,
|
|
scalars,
|
|
order,
|
|
typesig)
|
|
} |