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.
494 lines
12 KiB
494 lines
12 KiB
'use strict'
|
|
|
|
module.exports = boxIntersectIter
|
|
|
|
var pool = require('typedarray-pool')
|
|
var bits = require('bit-twiddle')
|
|
var bruteForce = require('./brute')
|
|
var bruteForcePartial = bruteForce.partial
|
|
var bruteForceFull = bruteForce.full
|
|
var sweep = require('./sweep')
|
|
var findMedian = require('./median')
|
|
var genPartition = require('./partition')
|
|
|
|
//Twiddle parameters
|
|
var BRUTE_FORCE_CUTOFF = 128 //Cut off for brute force search
|
|
var SCAN_CUTOFF = (1<<22) //Cut off for two way scan
|
|
var SCAN_COMPLETE_CUTOFF = (1<<22)
|
|
|
|
//Partition functions
|
|
var partitionInteriorContainsInterval = genPartition(
|
|
'!(lo>=p0)&&!(p1>=hi)',
|
|
['p0', 'p1'])
|
|
|
|
var partitionStartEqual = genPartition(
|
|
'lo===p0',
|
|
['p0'])
|
|
|
|
var partitionStartLessThan = genPartition(
|
|
'lo<p0',
|
|
['p0'])
|
|
|
|
var partitionEndLessThanEqual = genPartition(
|
|
'hi<=p0',
|
|
['p0'])
|
|
|
|
var partitionContainsPoint = genPartition(
|
|
'lo<=p0&&p0<=hi',
|
|
['p0'])
|
|
|
|
var partitionContainsPointProper = genPartition(
|
|
'lo<p0&&p0<=hi',
|
|
['p0'])
|
|
|
|
//Frame size for iterative loop
|
|
var IFRAME_SIZE = 6
|
|
var DFRAME_SIZE = 2
|
|
|
|
//Data for box statck
|
|
var INIT_CAPACITY = 1024
|
|
var BOX_ISTACK = pool.mallocInt32(INIT_CAPACITY)
|
|
var BOX_DSTACK = pool.mallocDouble(INIT_CAPACITY)
|
|
|
|
//Initialize iterative loop queue
|
|
function iterInit(d, count) {
|
|
var levels = (8 * bits.log2(count+1) * (d+1))|0
|
|
var maxInts = bits.nextPow2(IFRAME_SIZE*levels)
|
|
if(BOX_ISTACK.length < maxInts) {
|
|
pool.free(BOX_ISTACK)
|
|
BOX_ISTACK = pool.mallocInt32(maxInts)
|
|
}
|
|
var maxDoubles = bits.nextPow2(DFRAME_SIZE*levels)
|
|
if(BOX_DSTACK.length < maxDoubles) {
|
|
pool.free(BOX_DSTACK)
|
|
BOX_DSTACK = pool.mallocDouble(maxDoubles)
|
|
}
|
|
}
|
|
|
|
//Append item to queue
|
|
function iterPush(ptr,
|
|
axis,
|
|
redStart, redEnd,
|
|
blueStart, blueEnd,
|
|
state,
|
|
lo, hi) {
|
|
|
|
var iptr = IFRAME_SIZE * ptr
|
|
BOX_ISTACK[iptr] = axis
|
|
BOX_ISTACK[iptr+1] = redStart
|
|
BOX_ISTACK[iptr+2] = redEnd
|
|
BOX_ISTACK[iptr+3] = blueStart
|
|
BOX_ISTACK[iptr+4] = blueEnd
|
|
BOX_ISTACK[iptr+5] = state
|
|
|
|
var dptr = DFRAME_SIZE * ptr
|
|
BOX_DSTACK[dptr] = lo
|
|
BOX_DSTACK[dptr+1] = hi
|
|
}
|
|
|
|
//Special case: Intersect single point with list of intervals
|
|
function onePointPartial(
|
|
d, axis, visit, flip,
|
|
redStart, redEnd, red, redIndex,
|
|
blueOffset, blue, blueId) {
|
|
|
|
var elemSize = 2 * d
|
|
var bluePtr = blueOffset * elemSize
|
|
var blueX = blue[bluePtr + axis]
|
|
|
|
red_loop:
|
|
for(var i=redStart, redPtr=redStart*elemSize; i<redEnd; ++i, redPtr+=elemSize) {
|
|
var r0 = red[redPtr+axis]
|
|
var r1 = red[redPtr+axis+d]
|
|
if(blueX < r0 || r1 < blueX) {
|
|
continue
|
|
}
|
|
if(flip && blueX === r0) {
|
|
continue
|
|
}
|
|
var redId = redIndex[i]
|
|
for(var j=axis+1; j<d; ++j) {
|
|
var r0 = red[redPtr+j]
|
|
var r1 = red[redPtr+j+d]
|
|
var b0 = blue[bluePtr+j]
|
|
var b1 = blue[bluePtr+j+d]
|
|
if(r1 < b0 || b1 < r0) {
|
|
continue red_loop
|
|
}
|
|
}
|
|
var retval
|
|
if(flip) {
|
|
retval = visit(blueId, redId)
|
|
} else {
|
|
retval = visit(redId, blueId)
|
|
}
|
|
if(retval !== void 0) {
|
|
return retval
|
|
}
|
|
}
|
|
}
|
|
|
|
//Special case: Intersect one point with list of intervals
|
|
function onePointFull(
|
|
d, axis, visit,
|
|
redStart, redEnd, red, redIndex,
|
|
blueOffset, blue, blueId) {
|
|
|
|
var elemSize = 2 * d
|
|
var bluePtr = blueOffset * elemSize
|
|
var blueX = blue[bluePtr + axis]
|
|
|
|
red_loop:
|
|
for(var i=redStart, redPtr=redStart*elemSize; i<redEnd; ++i, redPtr+=elemSize) {
|
|
var redId = redIndex[i]
|
|
if(redId === blueId) {
|
|
continue
|
|
}
|
|
var r0 = red[redPtr+axis]
|
|
var r1 = red[redPtr+axis+d]
|
|
if(blueX < r0 || r1 < blueX) {
|
|
continue
|
|
}
|
|
for(var j=axis+1; j<d; ++j) {
|
|
var r0 = red[redPtr+j]
|
|
var r1 = red[redPtr+j+d]
|
|
var b0 = blue[bluePtr+j]
|
|
var b1 = blue[bluePtr+j+d]
|
|
if(r1 < b0 || b1 < r0) {
|
|
continue red_loop
|
|
}
|
|
}
|
|
var retval = visit(redId, blueId)
|
|
if(retval !== void 0) {
|
|
return retval
|
|
}
|
|
}
|
|
}
|
|
|
|
//The main box intersection routine
|
|
function boxIntersectIter(
|
|
d, visit, initFull,
|
|
xSize, xBoxes, xIndex,
|
|
ySize, yBoxes, yIndex) {
|
|
|
|
//Reserve memory for stack
|
|
iterInit(d, xSize + ySize)
|
|
|
|
var top = 0
|
|
var elemSize = 2 * d
|
|
var retval
|
|
|
|
iterPush(top++,
|
|
0,
|
|
0, xSize,
|
|
0, ySize,
|
|
initFull ? 16 : 0,
|
|
-Infinity, Infinity)
|
|
if(!initFull) {
|
|
iterPush(top++,
|
|
0,
|
|
0, ySize,
|
|
0, xSize,
|
|
1,
|
|
-Infinity, Infinity)
|
|
}
|
|
|
|
while(top > 0) {
|
|
top -= 1
|
|
|
|
var iptr = top * IFRAME_SIZE
|
|
var axis = BOX_ISTACK[iptr]
|
|
var redStart = BOX_ISTACK[iptr+1]
|
|
var redEnd = BOX_ISTACK[iptr+2]
|
|
var blueStart = BOX_ISTACK[iptr+3]
|
|
var blueEnd = BOX_ISTACK[iptr+4]
|
|
var state = BOX_ISTACK[iptr+5]
|
|
|
|
var dptr = top * DFRAME_SIZE
|
|
var lo = BOX_DSTACK[dptr]
|
|
var hi = BOX_DSTACK[dptr+1]
|
|
|
|
//Unpack state info
|
|
var flip = (state & 1)
|
|
var full = !!(state & 16)
|
|
|
|
//Unpack indices
|
|
var red = xBoxes
|
|
var redIndex = xIndex
|
|
var blue = yBoxes
|
|
var blueIndex = yIndex
|
|
if(flip) {
|
|
red = yBoxes
|
|
redIndex = yIndex
|
|
blue = xBoxes
|
|
blueIndex = xIndex
|
|
}
|
|
|
|
if(state & 2) {
|
|
redEnd = partitionStartLessThan(
|
|
d, axis,
|
|
redStart, redEnd, red, redIndex,
|
|
hi)
|
|
if(redStart >= redEnd) {
|
|
continue
|
|
}
|
|
}
|
|
if(state & 4) {
|
|
redStart = partitionEndLessThanEqual(
|
|
d, axis,
|
|
redStart, redEnd, red, redIndex,
|
|
lo)
|
|
if(redStart >= redEnd) {
|
|
continue
|
|
}
|
|
}
|
|
|
|
var redCount = redEnd - redStart
|
|
var blueCount = blueEnd - blueStart
|
|
|
|
if(full) {
|
|
if(d * redCount * (redCount + blueCount) < SCAN_COMPLETE_CUTOFF) {
|
|
retval = sweep.scanComplete(
|
|
d, axis, visit,
|
|
redStart, redEnd, red, redIndex,
|
|
blueStart, blueEnd, blue, blueIndex)
|
|
if(retval !== void 0) {
|
|
return retval
|
|
}
|
|
continue
|
|
}
|
|
} else {
|
|
if(d * Math.min(redCount, blueCount) < BRUTE_FORCE_CUTOFF) {
|
|
//If input small, then use brute force
|
|
retval = bruteForcePartial(
|
|
d, axis, visit, flip,
|
|
redStart, redEnd, red, redIndex,
|
|
blueStart, blueEnd, blue, blueIndex)
|
|
if(retval !== void 0) {
|
|
return retval
|
|
}
|
|
continue
|
|
} else if(d * redCount * blueCount < SCAN_CUTOFF) {
|
|
//If input medium sized, then use sweep and prune
|
|
retval = sweep.scanBipartite(
|
|
d, axis, visit, flip,
|
|
redStart, redEnd, red, redIndex,
|
|
blueStart, blueEnd, blue, blueIndex)
|
|
if(retval !== void 0) {
|
|
return retval
|
|
}
|
|
continue
|
|
}
|
|
}
|
|
|
|
//First, find all red intervals whose interior contains (lo,hi)
|
|
var red0 = partitionInteriorContainsInterval(
|
|
d, axis,
|
|
redStart, redEnd, red, redIndex,
|
|
lo, hi)
|
|
|
|
//Lower dimensional case
|
|
if(redStart < red0) {
|
|
|
|
if(d * (red0 - redStart) < BRUTE_FORCE_CUTOFF) {
|
|
//Special case for small inputs: use brute force
|
|
retval = bruteForceFull(
|
|
d, axis+1, visit,
|
|
redStart, red0, red, redIndex,
|
|
blueStart, blueEnd, blue, blueIndex)
|
|
if(retval !== void 0) {
|
|
return retval
|
|
}
|
|
} else if(axis === d-2) {
|
|
if(flip) {
|
|
retval = sweep.sweepBipartite(
|
|
d, visit,
|
|
blueStart, blueEnd, blue, blueIndex,
|
|
redStart, red0, red, redIndex)
|
|
} else {
|
|
retval = sweep.sweepBipartite(
|
|
d, visit,
|
|
redStart, red0, red, redIndex,
|
|
blueStart, blueEnd, blue, blueIndex)
|
|
}
|
|
if(retval !== void 0) {
|
|
return retval
|
|
}
|
|
} else {
|
|
iterPush(top++,
|
|
axis+1,
|
|
redStart, red0,
|
|
blueStart, blueEnd,
|
|
flip,
|
|
-Infinity, Infinity)
|
|
iterPush(top++,
|
|
axis+1,
|
|
blueStart, blueEnd,
|
|
redStart, red0,
|
|
flip^1,
|
|
-Infinity, Infinity)
|
|
}
|
|
}
|
|
|
|
//Divide and conquer phase
|
|
if(red0 < redEnd) {
|
|
|
|
//Cut blue into 3 parts:
|
|
//
|
|
// Points < mid point
|
|
// Points = mid point
|
|
// Points > mid point
|
|
//
|
|
var blue0 = findMedian(
|
|
d, axis,
|
|
blueStart, blueEnd, blue, blueIndex)
|
|
var mid = blue[elemSize * blue0 + axis]
|
|
var blue1 = partitionStartEqual(
|
|
d, axis,
|
|
blue0, blueEnd, blue, blueIndex,
|
|
mid)
|
|
|
|
//Right case
|
|
if(blue1 < blueEnd) {
|
|
iterPush(top++,
|
|
axis,
|
|
red0, redEnd,
|
|
blue1, blueEnd,
|
|
(flip|4) + (full ? 16 : 0),
|
|
mid, hi)
|
|
}
|
|
|
|
//Left case
|
|
if(blueStart < blue0) {
|
|
iterPush(top++,
|
|
axis,
|
|
red0, redEnd,
|
|
blueStart, blue0,
|
|
(flip|2) + (full ? 16 : 0),
|
|
lo, mid)
|
|
}
|
|
|
|
//Center case (the hard part)
|
|
if(blue0 + 1 === blue1) {
|
|
//Optimization: Range with exactly 1 point, use a brute force scan
|
|
if(full) {
|
|
retval = onePointFull(
|
|
d, axis, visit,
|
|
red0, redEnd, red, redIndex,
|
|
blue0, blue, blueIndex[blue0])
|
|
} else {
|
|
retval = onePointPartial(
|
|
d, axis, visit, flip,
|
|
red0, redEnd, red, redIndex,
|
|
blue0, blue, blueIndex[blue0])
|
|
}
|
|
if(retval !== void 0) {
|
|
return retval
|
|
}
|
|
} else if(blue0 < blue1) {
|
|
var red1
|
|
if(full) {
|
|
//If full intersection, need to handle special case
|
|
red1 = partitionContainsPoint(
|
|
d, axis,
|
|
red0, redEnd, red, redIndex,
|
|
mid)
|
|
if(red0 < red1) {
|
|
var redX = partitionStartEqual(
|
|
d, axis,
|
|
red0, red1, red, redIndex,
|
|
mid)
|
|
if(axis === d-2) {
|
|
//Degenerate sweep intersection:
|
|
// [red0, redX] with [blue0, blue1]
|
|
if(red0 < redX) {
|
|
retval = sweep.sweepComplete(
|
|
d, visit,
|
|
red0, redX, red, redIndex,
|
|
blue0, blue1, blue, blueIndex)
|
|
if(retval !== void 0) {
|
|
return retval
|
|
}
|
|
}
|
|
|
|
//Normal sweep intersection:
|
|
// [redX, red1] with [blue0, blue1]
|
|
if(redX < red1) {
|
|
retval = sweep.sweepBipartite(
|
|
d, visit,
|
|
redX, red1, red, redIndex,
|
|
blue0, blue1, blue, blueIndex)
|
|
if(retval !== void 0) {
|
|
return retval
|
|
}
|
|
}
|
|
} else {
|
|
if(red0 < redX) {
|
|
iterPush(top++,
|
|
axis+1,
|
|
red0, redX,
|
|
blue0, blue1,
|
|
16,
|
|
-Infinity, Infinity)
|
|
}
|
|
if(redX < red1) {
|
|
iterPush(top++,
|
|
axis+1,
|
|
redX, red1,
|
|
blue0, blue1,
|
|
0,
|
|
-Infinity, Infinity)
|
|
iterPush(top++,
|
|
axis+1,
|
|
blue0, blue1,
|
|
redX, red1,
|
|
1,
|
|
-Infinity, Infinity)
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if(flip) {
|
|
red1 = partitionContainsPointProper(
|
|
d, axis,
|
|
red0, redEnd, red, redIndex,
|
|
mid)
|
|
} else {
|
|
red1 = partitionContainsPoint(
|
|
d, axis,
|
|
red0, redEnd, red, redIndex,
|
|
mid)
|
|
}
|
|
if(red0 < red1) {
|
|
if(axis === d-2) {
|
|
if(flip) {
|
|
retval = sweep.sweepBipartite(
|
|
d, visit,
|
|
blue0, blue1, blue, blueIndex,
|
|
red0, red1, red, redIndex)
|
|
} else {
|
|
retval = sweep.sweepBipartite(
|
|
d, visit,
|
|
red0, red1, red, redIndex,
|
|
blue0, blue1, blue, blueIndex)
|
|
}
|
|
} else {
|
|
iterPush(top++,
|
|
axis+1,
|
|
red0, red1,
|
|
blue0, blue1,
|
|
flip,
|
|
-Infinity, Infinity)
|
|
iterPush(top++,
|
|
axis+1,
|
|
blue0, blue1,
|
|
red0, red1,
|
|
flip^1,
|
|
-Infinity, Infinity)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} |