Remove other neighbour sampling

This commit is contained in:
Pitchaya Boonsarngsuk
2018-01-31 21:17:08 +00:00
parent ab5d22ce1d
commit b6c730ead1
4 changed files with 1 additions and 461 deletions

View File

@@ -9,7 +9,7 @@ function startNeighbourSamplingSimulation() {
simulation.stop(); simulation.stop();
p1 = performance.now(); p1 = performance.now();
let force = d3.forceNeighbourSamplingDistance() let force = d3.forceNeighbourSampling()
.neighbourSize(NEIGHBOUR_SIZE) .neighbourSize(NEIGHBOUR_SIZE)
.sampleSize(SAMPLE_SIZE) .sampleSize(SAMPLE_SIZE)
.distance(function (s, t) { .distance(function (s, t) {

View File

@@ -1,10 +1,4 @@
/*
export {default as forceNeighbourSampling} export {default as forceNeighbourSampling}
from "./src/neighbourSampling";
export {default as forceNeighbourSamplingPre}
from "./src/neighbourSamplingPre";
*/
export {default as forceNeighbourSamplingDistance}
from "./src/neighbourSamplingDistance"; from "./src/neighbourSamplingDistance";
/* /*
export { default as forceBarnesHut} export { default as forceBarnesHut}

View File

@@ -1,257 +0,0 @@
import constant from "./constant";
import jiggle from "./jiggle";
/**
* Set the node id accessor to the specified i.
* @param {node} d - node.
* @param {accessor} i - id accessor.
* @return {accessor} - node id accessor.
*/
function index(d, i) {
return i;
}
/**
* The implementation of Chalmers' 1996 Neighbour and Sampling algorithm.
* It uses random sampling to find the most suited neighbours from the
* data set.
* @return {force} calculated forces.
*/
export default function () {
var id = index,
neighbours = [],
samples = new Array(),
distance = constant(300),
nodes,
neighbourSize = 6,
sampleSize = 3,
freeness = 0.85,
springForce = 0.7,
dampingFactor = 0.3,
velocity,
multiplier = 50;
/**
* Calculates the forces at each iteration between the node and the
* objects in neighbour and sample sets.
* @param {number} alpha - controls the stopping of the
* particle simulations.
*/
function force(alpha) {
velocity = 0;
for (var i = 0, n = nodes.length; i < n; ++i) {
// Randomize the samples for every node.
samples[i] = randomizeSample(i);
// Calculate the forces between node and its neighbours.
for (var [keyN, valueN] of neighbours[i]) {
setVelocity(i, keyN, valueN, alpha);
}
// Calculate the forces between node and its sample set.
for (var [keyS, valueS] of samples[i]) {
setVelocity(i, keyS, valueS, alpha);
}
// Check if there are a better neighbours in a sample array
// for each node.
findNewNeighbours(i);
}
}
/**
* Set the velocities of the source and target nodes.
* @param {number} sourceId - source node id.
* @param {number} targetId - target node id.
* @param {number} dist - high dimensional distance between
* the two nodes.
* @param {number} alpha - controls the speed of simulation.
*/
function setVelocity(sourceId, targetId, dist, alpha) {
var source, target, x, y, l;
source = nodes[sourceId], target = nodes[targetId];
// If x or y coordinates not defined, add some randomness.
x = target.x + target.vx - source.x - source.vx || jiggle();
y = target.y + target.vy - source.y - source.vy || jiggle();
l = Math.sqrt(x * x + y * y);
l = (l - dist * multiplier) / l * alpha;
x *= l, y *= l;
velocity += x + y;
// Set the calculated velocites for both nodes.
target.vx -= x;
target.vy -= y;
source.vx += x;
source.vy += y;
}
/**
* Initialize the neighbour and sample set at the start.
*/
function initialize() {
if (!nodes) return;
// Initialize for each node a neighbour and sample arrays
// with random values.
for (var i = 0, n = nodes.length; i < n; ++i) {
var exclude = []; // Array that keeps the indices of nodes to ignore.
exclude.push(i);
var neighbs = createRandomSample(i, exclude, n, neighbourSize);
// Sort the neighbour set by the distances.
neighbs = new Map([...neighbs.entries()].sort(sortDistances));
neighbours[i] = neighbs;
exclude.concat(neighbs);
samples[i] = createRandomSample(i, exclude, n, sampleSize);
}
}
/**
* Function that compares to map elements by its values.
* @param {object} a
* @param {object} b
* @return {number} - 0, if values are equal, positive number if b > a,
* negative otherwise.
*/
function sortDistances(a, b) {
return b[1] - a[1];
}
/**
* Create an array of random integers, all different, with maximum
* value max and with size elements. No elements from exlucde should
* be included.
* @param {number} index - index of current node.
* @param {array} exclude - indices of the nodes to ignore.
* @param {number} max - maximum value.
* @param {number} size - the number of elements in map to return.
* @return {map} - a created map that contains random elements from
* data set.
*/
function createRandomSample(index, exclude, max, size) {
var randElements = new Map();
for (var i = 0; i < size; ++i) {
// Stop when no new elements can be found.
if (randElements.size + exclude.length >= nodes.length) {
break;
}
var rand = Math.floor((Math.random() * max));
// If the rand is already in random list or in exclude list
// ignore it and get a new value.
while (randElements.has(rand) || exclude.includes(rand)) {
rand = Math.floor((Math.random() * max));
}
randElements.set(rand, +distance(nodes[index], nodes[rand]));
}
return randElements;
}
/**
* Creates a new map of random numbers to be used by the samples list.
* @param {number} index - index of current node.
* @return {map} - map that contains random elements from data set.
*/
function randomizeSample(index) {
// Ignore the current neighbours of the node and itself.
var exclude = [index];
exclude = exclude.concat(Array.from(neighbours[index].keys()));
return createRandomSample(index, exclude, nodes.length, sampleSize);
}
/**
* Compares the elements from sample set to the neighbour set and
* replaces the elements from neighbour set if better neighbours are
* found in sample set.
* @param {number} index - index of current node.
*/
function findNewNeighbours(index) {
var sample = samples[index];
for (var [key, value] of sample) {
var neighbMax = neighbours[index].entries().next().value;
// Check if a value from sample could be a better neighbour
// if so, replace it.
if (value < neighbMax[1]) {
neighbours[index].delete(neighbMax[0]);
neighbours[index].set(key, value)
neighbours[index] = new Map([...neighbours[index].entries()].sort(sortDistances));
}
}
}
/**
* Calculates the stress. Basically, it computes the difference between
* high dimensional distance and real distance. The lower the stress is,
* the better layout.
* @return {number} - stress of the layout.
*/
function getStress() {
var totalDiffSq = 0, totalHighDistSq = 0;
for (var i = 0, source, target, realDist, highDist; i < nodes.length; i++) {
for (var j = 0; j < nodes.length; j++) {
if (i !== j) {
source = nodes[i], target = nodes[j];
realDist = Math.hypot(target.x - source.x, target.y - source.y);
highDist = +distance(source, target);
totalDiffSq += Math.pow(realDist - highDist, 2);
totalHighDistSq += highDist * highDist;
}
}
}
return Math.sqrt(totalDiffSq / totalHighDistSq);
}
/**
* Calculates the average velocity of the force calculation at the
* current iteration.
* @return {number} - average velocity.
*/
function getAvgVelocity() {
return velocity / ((neighbourSize + sampleSize) * nodes.length);
}
// API for initializing the algorithm, setting parameters and querying
// metrics.
force.initialize = function (_) {
nodes = _;
initialize();
};
force.id = function (_) {
return arguments.length ? (id = _, force) : id;
};
force.neighbourSize = function (_) {
return arguments.length ? (neighbourSize = +_, force) : neighbourSize;
};
force.sampleSize = function (_) {
return arguments.length ? (sampleSize = +_, force) : sampleSize;
};
force.distance = function (_) {
return arguments.length ? (distance = typeof _ === "function" ? _ : constant(+_), force) : distance;
};
force.stress = function () {
return getStress();
};
force.velocity = function () {
return getAvgVelocity();
};
force.freeness = function (_) {
return arguments.length ? (freeness = +_, force) : freeness;
};
force.nodeNeighbours = function (_) {
return arguments.length ? neighbours[+_] : [];
};
force.multiplier = function (_) {
return arguments.length ? (multiplier = +_, force) : multiplier;
};
return force;
}

View File

@@ -1,197 +0,0 @@
import constant from "./constant";
import jiggle from "./jiggle";
/**
* Set the node id accessor to the specified i.
* @param {node} d - node.
* @param {accessor} i - id accessor.
* @return {accessor} - node id accessor.
*/
function index(d, i) {
return i;
}
/**
* The implementation of Chalmers' 1996 Neighbour and Sampling algorithm.
* It uses random sampling to find the most suited neighbours from the
* data set.
* @return {force} calculated forces.
*/
export default function () {
var id = index,
neighbours = [],
worst = [],
samples = new Array(),
distance = constant(300),
nodes,
neighbourSize = 6,
sampleSize = 3,
freeness = 0.85,
springForce = 0.7,
dampingFactor = 0.3;
/**
* Calculates the forces at each iteration between the node and the
* objects in neighbour and sample sets.
* @param {number} alpha - controls the stopping of the
* particle simulations.
*/
function force(alpha) {
for (var i = 0, n = nodes.length; i < n; ++i) {
// Randomize the samples for every node.
// Calculate the forces between node and its neighbours.
for (var [keyN, valueN] of neighbours[i]) {
setVelocity(i, keyN, valueN, alpha);
}
// Calculate the forces between node and its sample set.
// for (var [keyS, valueS] of worst[i]) {
// setVelocity(i, keyS, valueS, alpha);
// }
}
}
/**
* Set the velocities of the source and target nodes.
* @param {number} sourceId - source node id.
* @param {number} targetId - target node id.
* @param {number} dist - high dimensional distance between
* the two nodes.
* @param {number} alpha - controls the speed of simulation.
*/
function setVelocity(sourceId, targetId, dist, alpha) {
var source, target, x, y, l;
source = nodes[sourceId], target = nodes[targetId];
// If x or y coordinates not defined, add some randomness.
x = target.x + target.vx - source.x - source.vx || jiggle();
y = target.y + target.vy - source.y - source.vy || jiggle();
l = Math.sqrt(x * x + y * y);
l = (l - dist) / l * alpha;
x *= l, y *= l;
// Set the calculated velocites for both nodes.
target.vx -= x;
target.vy -= y;
source.vx += x;
source.vy += y;
}
/**
* Initialize the neighbour and sample set at the start.
*/
function initialize() {
if (!nodes) return;
findNeighbours();
}
/**
* Function that compares to map elements by its values.
* @param {object} a
* @param {object} b
* @return {number} - 0, if values are equal, positive number if b > a,
* negative otherwise.
*/
function sortDistances(a, b) {
return b[1] - a[1];
}
function findNeighbours() {
// Initialize for each node a neighbour and sample arrays
// with random values.
for (var i = 0, n = nodes.length; i < n; ++i) {
neighbours[i] = new Map();
for (var j = 0; j < n; j++) {
if (i !== j) {
var dist = +distance(nodes[i], nodes[j]);
if (neighbours[i].size < neighbourSize) {
neighbours[i].set(j, dist);
neighbours[i] = new Map([...neighbours[i].entries()].sort(sortDistances));
} else {
var neighbMax = neighbours[i].entries().next().value;
if (dist < neighbMax[1]) {
neighbours[i].delete(neighbMax[0]);
neighbours[i].set(j, dist);
neighbours[i] = new Map([...neighbours[i].entries()].sort(sortDistances));
}
}
}
}
}
}
/**
* Calculates the stress. Basically, it computes the difference between
* high dimensional distance and real distance. The lower the stress is,
* the better layout.
* @return {number} - stress of the layout.
*/
function getStress() {
var totalDiffSq = 0, totalHighDistSq = 0;
for (var i = 0, source, target, realDist, highDist; i < nodes.length; i++) {
for (var j = 0; j < nodes.length; j++) {
if (i !== j) {
source = nodes[i], target = nodes[j];
realDist = Math.hypot(target.x - source.x, target.y - source.y);
highDist = +distance(source, target);
totalDiffSq += Math.pow(realDist - highDist, 2);
totalHighDistSq += highDist * highDist;
}
}
}
return Math.sqrt(totalDiffSq / totalHighDistSq);
}
/**
* Calculates the average velocity of the force calculation at the
* current iteration.
* @return {number} - average velocity.
*/
function getAvgVelocity() {
return velocity / ((neighbourSize + sampleSize) * nodes.length);
}
// API for initializing the algorithm, setting parameters and querying
// metrics.
force.initialize = function (_) {
nodes = _;
initialize();
};
force.id = function (_) {
return arguments.length ? (id = _, force) : id;
};
force.neighbourSize = function (_) {
return arguments.length ? (neighbourSize = +_, force) : neighbourSize;
};
force.sampleSize = function (_) {
return arguments.length ? (sampleSize = +_, force) : sampleSize;
};
force.distance = function (_) {
return arguments.length ? (distance = typeof _ === "function" ? _ : constant(+_), force) : distance;
};
force.stress = function () {
return getStress();
};
force.velocity = function () {
return getAvgVelocity();
};
force.freeness = function (_) {
return arguments.length ? (freeness = +_, force) : freeness;
};
force.nodeNeighbours = function (_) {
return arguments.length ? neighbours[+_] : [];
};
return force;
}