hdsp2
Version:
High-Dimensional Space Projections
254 lines • 9.54 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SparseMDSSGD = void 0;
var Edge_1 = __importDefault(require("./Edge"));
var fibonacci_heap_1 = require("@tyriar/fibonacci-heap");
var SparseTerm_1 = __importDefault(require("./SparseTerm"));
var Utils_1 = require("./Utils");
var SparseMDSSGD = (function () {
function SparseMDSSGD() {
}
SparseMDSSGD.project = function (n, m, I, J, V, D, p, seed, t_max, eps) {
console.debug("Building weighted graph...");
var graph = SparseMDSSGD.buildGraphWeighted(n, m, I, J, V);
console.debug("Sampling pivots...");
var closest_pivots = SparseMDSSGD.maxmin_random_sp_weighted(graph, p, 0, seed);
console.debug("Configuring terms...");
var terms = SparseMDSSGD.MSSP_weighted(graph, closest_pivots);
console.debug("Configuring schedule...");
var etas = SparseMDSSGD.schedule(terms, t_max, eps);
console.debug("Initializing layout...");
var X = Utils_1.Utils.random2D(n, D);
console.debug("Computing layout...");
SparseMDSSGD.sgd(X, D, terms, etas, seed);
return X;
};
SparseMDSSGD.sgd = function (X, D, terms, etas, seed) {
for (var i_eta = 0; i_eta < etas.length; i_eta++) {
var eta = etas[i_eta];
SparseMDSSGD.fisheryates_shuffle(terms);
var n_terms = terms.length;
for (var i_term = 0; i_term < n_terms; i_term++) {
var t = terms[i_term];
var i = t.i;
var j = t.j;
var d_ij = t.d;
var mu_i = eta * t.w_ij;
if (mu_i > 1) {
mu_i = 1;
}
var mu_j = eta * t.w_ji;
if (mu_j > 1) {
mu_j = 1;
}
var dx = X[i][0] - X[j][0];
var dy = X[i][1] - X[j][1];
var dz = X[i][2] - X[j][2];
var mag = Math.sqrt(dx * dx + dy * dy + dz * dz);
var r = (mag - d_ij) / (2 * mag);
var r_x = r * dx;
var r_y = r * dy;
var r_z = r * dz;
X[i][0] -= mu_i * r_x;
X[i][1] -= mu_i * r_y;
X[i][2] -= mu_i * r_z;
X[j][0] += mu_j * r_x;
X[j][1] += mu_j * r_y;
X[j][2] += mu_j * r_z;
}
}
};
SparseMDSSGD.fisheryates_shuffle = function (terms) {
var n = terms.length;
for (var i = n - 1; i >= 1; i--) {
var j = SparseMDSSGD.random_int(0, i);
var temp = terms[i];
terms[i] = terms[j];
terms[j] = temp;
}
};
SparseMDSSGD.random_int = function (min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
};
SparseMDSSGD.schedule = function (terms, t_max, eps) {
var w_min = Number.MAX_VALUE;
var w_max = Number.MIN_VALUE;
for (var i = 0; i < terms.length; i++) {
var term = terms[i];
if (term.w_ij < w_min && term.w_ij !== 0) {
w_min = term.w_ij;
}
if (term.w_ji < w_min && term.w_ji !== 0) {
w_min = term.w_ji;
}
if (term.w_ij > w_max) {
w_max = term.w_ij;
}
if (term.w_ji > w_max) {
w_max = term.w_ji;
}
}
var eta_max = 1.0 / w_min;
var eta_min = eps / w_max;
var lambda = Math.log(eta_max / eta_min) / (t_max - 1);
var etas = new Array();
for (var t = 0; t < t_max; t++) {
etas.push(eta_max * Math.exp(-lambda * t));
}
console.debug("Etas: " + etas);
return etas;
};
SparseMDSSGD.MSSP_weighted = function (graph, closest_pivots) {
var n = graph.length;
var regions = new Map();
var termsDict = new Map();
for (var i = 0; i < n; i++) {
if (!regions.has(closest_pivots[i])) {
regions.set(closest_pivots[i], new Set());
}
regions.get(closest_pivots[i]).add(i);
}
for (var i = 0; i < n; i++) {
for (var i_edge = 0; i_edge < graph[i].length; i_edge++) {
var e = graph[i][i_edge];
var j = e.target;
var d_ij = e.weight;
if (i < j) {
if (!termsDict.has(i)) {
termsDict.set(i, new Map());
}
if (!termsDict.get(i).has(j)) {
termsDict.get(i).set(j, new SparseTerm_1.default(i, j, d_ij));
}
else {
termsDict.get(i).get(j).d = d_ij;
}
if (d_ij === 5000) {
termsDict.get(i).get(j).w_ij = termsDict.get(i).get(j).w_ji = 5000;
}
else {
termsDict.get(i).get(j).w_ij = termsDict.get(i).get(j).w_ji = 1 / (d_ij * d_ij);
}
}
}
}
var terms = new Array();
termsDict.forEach(function (i_terms) {
i_terms.forEach(function (term) {
terms.push(term);
});
});
return terms;
};
SparseMDSSGD.maxmin_random_sp_weighted = function (graph, n_pivots, p0, seed) {
var n = graph.length;
var mins = new Array(n).fill(Number.MAX_VALUE);
var argmins = new Array(n).fill(-1);
mins[p0] = 0;
argmins[p0] = p0;
SparseMDSSGD.maxmin_bfs_weighted(graph, p0, mins, argmins);
for (var i = 0; i < n; i++) {
if (argmins[i] === -1) {
throw new Error("graph has multiple connected components");
}
}
for (var i = 1; i < n_pivots; i++) {
var min_total = 0;
for (var j = 0; j < n; j++) {
min_total += mins[j];
}
var sample = Math.random() * min_total;
var cumul = 0;
var argmax = -1;
for (var j = 0; j < n; j++) {
cumul += mins[j];
if (cumul >= sample) {
argmax = j;
break;
}
}
if (argmax === -1) {
throw new Error("weighted pivot sampling failed");
}
mins[argmax] = 0;
argmins[argmax] = argmax;
SparseMDSSGD.maxmin_bfs_weighted(graph, argmax, mins, argmins);
}
return argmins;
};
SparseMDSSGD.maxmin_bfs_weighted = function (graph, p, mins, argmins) {
var n = graph.length;
var visited = new Array(n).fill(false);
var d = new Array(n).fill(Number.MAX_VALUE);
var pq = new fibonacci_heap_1.FibonacciHeap(function (a, b) {
return a.key.weight > b.key.weight ? 1 : a.key.weight < b.key.weight ? -1 : 0;
});
d[p] = 0;
pq.insert(new Edge_1.default(p, 0));
while (!pq.isEmpty()) {
var current = pq.findMinimum().key.target;
var d_pi = pq.findMinimum().key.weight;
pq.extractMinimum();
if (!visited[current]) {
visited[current] = true;
if (d_pi < mins[current]) {
mins[current] = d_pi;
argmins[current] = p;
}
for (var i_edge = 0; i_edge < graph[current].length; i_edge++) {
var e = graph[current][i_edge];
var next = e.target;
var weight = e.weight;
if (d_pi + weight < d[next]) {
d[next] = d_pi + weight;
pq.insert(new Edge_1.default(next, d[next]));
}
}
}
}
};
SparseMDSSGD.buildGraphWeighted = function (n, m, I, J, V) {
var undirected = new Array(n);
var graph = new Array(n);
for (var ij = 0; ij < m; ij++) {
var i = I[ij], j = J[ij];
if (i >= n || j >= n) {
throw new Error("i or j bigger than n");
}
var v = V[ij];
if (v <= 0) {
throw new Error("edge length less than or equal to 0");
}
if (!undirected[i]) {
undirected[i] = new Map();
}
if (!undirected[j]) {
undirected[j] = new Map();
}
if (!graph[i]) {
graph[i] = new Array();
}
if (!graph[j]) {
graph[j] = new Array();
}
if (i !== j && !undirected[j].has(i)) {
undirected[i].set(j, v);
undirected[j].set(i, v);
graph[i].push(new Edge_1.default(j, v));
graph[j].push(new Edge_1.default(i, v));
}
else {
if (undirected[j].get(i) !== v) {
throw new Error("graph edge lengths not symmetric");
}
}
}
return graph;
};
return SparseMDSSGD;
}());
exports.SparseMDSSGD = SparseMDSSGD;
//# sourceMappingURL=SparseMDSSGD.js.map