@elarsaks/umap-wasm 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +349 -0
- package/dist/src/heap.d.ts +12 -0
- package/dist/src/heap.js +226 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +5 -0
- package/dist/src/lib.d.ts +1 -0
- package/dist/src/lib.js +5 -0
- package/dist/src/matrix.d.ts +41 -0
- package/dist/src/matrix.js +360 -0
- package/dist/src/nn_descent.d.ts +17 -0
- package/dist/src/nn_descent.js +204 -0
- package/dist/src/tree.d.ts +16 -0
- package/dist/src/tree.js +320 -0
- package/dist/src/umap.d.ts +102 -0
- package/dist/src/umap.js +842 -0
- package/dist/src/utils.d.ts +16 -0
- package/dist/src/utils.js +137 -0
- package/dist/src/wasmBridge.d.ts +57 -0
- package/dist/src/wasmBridge.js +290 -0
- package/dist/test/matrix.test.d.ts +1 -0
- package/dist/test/matrix.test.js +169 -0
- package/dist/test/nn_descent.test.d.ts +1 -0
- package/dist/test/nn_descent.test.js +58 -0
- package/dist/test/test_data.d.ts +13 -0
- package/dist/test/test_data.js +1054 -0
- package/dist/test/tree.test.d.ts +1 -0
- package/dist/test/tree.test.js +60 -0
- package/dist/test/umap.test.d.ts +1 -0
- package/dist/test/umap.test.js +293 -0
- package/dist/test/utils.test.d.ts +1 -0
- package/dist/test/utils.test.js +128 -0
- package/dist/test/wasmDistance.test.d.ts +1 -0
- package/dist/test/wasmDistance.test.js +124 -0
- package/dist/test/wasmMatrix.test.d.ts +1 -0
- package/dist/test/wasmMatrix.test.js +389 -0
- package/dist/test/wasmTree.test.d.ts +1 -0
- package/dist/test/wasmTree.test.js +212 -0
- package/lib/umap-js.js +8657 -0
- package/lib/umap-js.min.js +1 -0
- package/package.json +58 -0
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __values = (this && this.__values) || function(o) {
|
|
36
|
+
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
|
|
37
|
+
if (m) return m.call(o);
|
|
38
|
+
if (o && typeof o.length === "number") return {
|
|
39
|
+
next: function () {
|
|
40
|
+
if (o && i >= o.length) o = void 0;
|
|
41
|
+
return { value: o && o[i++], done: !o };
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
|
|
45
|
+
};
|
|
46
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
47
|
+
exports.makeNNDescent = makeNNDescent;
|
|
48
|
+
exports.makeInitializations = makeInitializations;
|
|
49
|
+
exports.makeInitializedNNSearch = makeInitializedNNSearch;
|
|
50
|
+
exports.initializeSearch = initializeSearch;
|
|
51
|
+
var heap = __importStar(require("./heap"));
|
|
52
|
+
var matrix = __importStar(require("./matrix"));
|
|
53
|
+
var tree = __importStar(require("./tree"));
|
|
54
|
+
var utils = __importStar(require("./utils"));
|
|
55
|
+
function makeNNDescent(distanceFn, random) {
|
|
56
|
+
return function nNDescent(data, leafArray, nNeighbors, nIters, maxCandidates, delta, rho, rpTreeInit) {
|
|
57
|
+
if (nIters === void 0) { nIters = 10; }
|
|
58
|
+
if (maxCandidates === void 0) { maxCandidates = 50; }
|
|
59
|
+
if (delta === void 0) { delta = 0.001; }
|
|
60
|
+
if (rho === void 0) { rho = 0.5; }
|
|
61
|
+
if (rpTreeInit === void 0) { rpTreeInit = true; }
|
|
62
|
+
var nVertices = data.length;
|
|
63
|
+
var currentGraph = heap.makeHeap(data.length, nNeighbors);
|
|
64
|
+
for (var i = 0; i < data.length; i++) {
|
|
65
|
+
var indices = heap.rejectionSample(nNeighbors, data.length, random);
|
|
66
|
+
for (var j = 0; j < indices.length; j++) {
|
|
67
|
+
var d = distanceFn(data[i], data[indices[j]]);
|
|
68
|
+
heap.heapPush(currentGraph, i, d, indices[j], 1);
|
|
69
|
+
heap.heapPush(currentGraph, indices[j], d, i, 1);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (rpTreeInit) {
|
|
73
|
+
for (var n = 0; n < leafArray.length; n++) {
|
|
74
|
+
for (var i = 0; i < leafArray[n].length; i++) {
|
|
75
|
+
if (leafArray[n][i] < 0) {
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
for (var j = i + 1; j < leafArray[n].length; j++) {
|
|
79
|
+
if (leafArray[n][j] < 0) {
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
var d = distanceFn(data[leafArray[n][i]], data[leafArray[n][j]]);
|
|
83
|
+
heap.heapPush(currentGraph, leafArray[n][i], d, leafArray[n][j], 1);
|
|
84
|
+
heap.heapPush(currentGraph, leafArray[n][j], d, leafArray[n][i], 1);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
for (var n = 0; n < nIters; n++) {
|
|
90
|
+
var candidateNeighbors = heap.buildCandidates(currentGraph, nVertices, nNeighbors, maxCandidates, random);
|
|
91
|
+
var c = 0;
|
|
92
|
+
for (var i = 0; i < nVertices; i++) {
|
|
93
|
+
for (var j = 0; j < maxCandidates; j++) {
|
|
94
|
+
var p = Math.floor(candidateNeighbors[0][i][j]);
|
|
95
|
+
if (p < 0 || utils.tauRand(random) < rho) {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
for (var k = 0; k < maxCandidates; k++) {
|
|
99
|
+
var q = Math.floor(candidateNeighbors[0][i][k]);
|
|
100
|
+
var cj = candidateNeighbors[2][i][j];
|
|
101
|
+
var ck = candidateNeighbors[2][i][k];
|
|
102
|
+
if (q < 0 || (!cj && !ck)) {
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
var d = distanceFn(data[p], data[q]);
|
|
106
|
+
c += heap.heapPush(currentGraph, p, d, q, 1);
|
|
107
|
+
c += heap.heapPush(currentGraph, q, d, p, 1);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (c <= delta * nNeighbors * data.length) {
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
var sorted = heap.deheapSort(currentGraph);
|
|
116
|
+
return sorted;
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
function makeInitializations(distanceFn) {
|
|
120
|
+
function initFromRandom(nNeighbors, data, queryPoints, _heap, random) {
|
|
121
|
+
for (var i = 0; i < queryPoints.length; i++) {
|
|
122
|
+
var indices = utils.rejectionSample(nNeighbors, data.length, random);
|
|
123
|
+
for (var j = 0; j < indices.length; j++) {
|
|
124
|
+
if (indices[j] < 0) {
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
var d = distanceFn(data[indices[j]], queryPoints[i]);
|
|
128
|
+
heap.heapPush(_heap, i, d, indices[j], 1);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function initFromTree(_tree, data, queryPoints, _heap, random) {
|
|
133
|
+
for (var i = 0; i < queryPoints.length; i++) {
|
|
134
|
+
var indices = tree.searchFlatTree(queryPoints[i], _tree, random);
|
|
135
|
+
for (var j = 0; j < indices.length; j++) {
|
|
136
|
+
if (indices[j] < 0) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
var d = distanceFn(data[indices[j]], queryPoints[i]);
|
|
140
|
+
heap.heapPush(_heap, i, d, indices[j], 1);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
return { initFromRandom: initFromRandom, initFromTree: initFromTree };
|
|
146
|
+
}
|
|
147
|
+
function makeInitializedNNSearch(distanceFn) {
|
|
148
|
+
return function nnSearchFn(data, graph, initialization, queryPoints) {
|
|
149
|
+
var e_1, _a;
|
|
150
|
+
var _b = matrix.getCSR(graph), indices = _b.indices, indptr = _b.indptr;
|
|
151
|
+
for (var i = 0; i < queryPoints.length; i++) {
|
|
152
|
+
var tried = new Set(initialization[0][i]);
|
|
153
|
+
while (true) {
|
|
154
|
+
var vertex = heap.smallestFlagged(initialization, i);
|
|
155
|
+
if (vertex === -1) {
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
var candidates = indices.slice(indptr[vertex], indptr[vertex + 1]);
|
|
159
|
+
try {
|
|
160
|
+
for (var candidates_1 = (e_1 = void 0, __values(candidates)), candidates_1_1 = candidates_1.next(); !candidates_1_1.done; candidates_1_1 = candidates_1.next()) {
|
|
161
|
+
var candidate = candidates_1_1.value;
|
|
162
|
+
if (candidate === vertex ||
|
|
163
|
+
candidate === -1 ||
|
|
164
|
+
tried.has(candidate)) {
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
var d = distanceFn(data[candidate], queryPoints[i]);
|
|
168
|
+
heap.uncheckedHeapPush(initialization, i, d, candidate, 1);
|
|
169
|
+
tried.add(candidate);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
173
|
+
finally {
|
|
174
|
+
try {
|
|
175
|
+
if (candidates_1_1 && !candidates_1_1.done && (_a = candidates_1.return)) _a.call(candidates_1);
|
|
176
|
+
}
|
|
177
|
+
finally { if (e_1) throw e_1.error; }
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return initialization;
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
function initializeSearch(forest, data, queryPoints, nNeighbors, initFromRandom, initFromTree, random) {
|
|
185
|
+
var e_2, _a;
|
|
186
|
+
var results = heap.makeHeap(queryPoints.length, nNeighbors);
|
|
187
|
+
initFromRandom(nNeighbors, data, queryPoints, results, random);
|
|
188
|
+
if (forest) {
|
|
189
|
+
try {
|
|
190
|
+
for (var forest_1 = __values(forest), forest_1_1 = forest_1.next(); !forest_1_1.done; forest_1_1 = forest_1.next()) {
|
|
191
|
+
var tree_1 = forest_1_1.value;
|
|
192
|
+
initFromTree(tree_1, data, queryPoints, results, random);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
|
196
|
+
finally {
|
|
197
|
+
try {
|
|
198
|
+
if (forest_1_1 && !forest_1_1.done && (_a = forest_1.return)) _a.call(forest_1);
|
|
199
|
+
}
|
|
200
|
+
finally { if (e_2) throw e_2.error; }
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return results;
|
|
204
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { RandomFn, Vector, Vectors } from './umap';
|
|
2
|
+
import { WasmFlatTree } from './wasmBridge';
|
|
3
|
+
export declare class FlatTree {
|
|
4
|
+
hyperplanes: number[][];
|
|
5
|
+
offsets: number[];
|
|
6
|
+
children: number[][];
|
|
7
|
+
indices: number[][];
|
|
8
|
+
private wasmTree?;
|
|
9
|
+
constructor(hyperplanes: number[][], offsets: number[], children: number[][], indices: number[][]);
|
|
10
|
+
static fromWasm(wasmTree: WasmFlatTree): FlatTree;
|
|
11
|
+
getWasmTree(): WasmFlatTree | undefined;
|
|
12
|
+
dispose(): void;
|
|
13
|
+
}
|
|
14
|
+
export declare function makeForest(data: Vectors, nNeighbors: number, nTrees: number, random: RandomFn, useWasm?: boolean): FlatTree[];
|
|
15
|
+
export declare function makeLeafArray(rpForest: FlatTree[]): number[][];
|
|
16
|
+
export declare function searchFlatTree(point: Vector, tree: FlatTree, random: RandomFn): number[];
|
package/dist/src/tree.js
ADDED
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __read = (this && this.__read) || function (o, n) {
|
|
36
|
+
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
|
37
|
+
if (!m) return o;
|
|
38
|
+
var i = m.call(o), r, ar = [], e;
|
|
39
|
+
try {
|
|
40
|
+
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
|
41
|
+
}
|
|
42
|
+
catch (error) { e = { error: error }; }
|
|
43
|
+
finally {
|
|
44
|
+
try {
|
|
45
|
+
if (r && !r.done && (m = i["return"])) m.call(i);
|
|
46
|
+
}
|
|
47
|
+
finally { if (e) throw e.error; }
|
|
48
|
+
}
|
|
49
|
+
return ar;
|
|
50
|
+
};
|
|
51
|
+
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
52
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
53
|
+
if (ar || !(i in from)) {
|
|
54
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
55
|
+
ar[i] = from[i];
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
59
|
+
};
|
|
60
|
+
var __values = (this && this.__values) || function(o) {
|
|
61
|
+
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
|
|
62
|
+
if (m) return m.call(o);
|
|
63
|
+
if (o && typeof o.length === "number") return {
|
|
64
|
+
next: function () {
|
|
65
|
+
if (o && i >= o.length) o = void 0;
|
|
66
|
+
return { value: o && o[i++], done: !o };
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
|
|
70
|
+
};
|
|
71
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
72
|
+
exports.FlatTree = void 0;
|
|
73
|
+
exports.makeForest = makeForest;
|
|
74
|
+
exports.makeLeafArray = makeLeafArray;
|
|
75
|
+
exports.searchFlatTree = searchFlatTree;
|
|
76
|
+
var utils = __importStar(require("./utils"));
|
|
77
|
+
var wasmBridge_1 = require("./wasmBridge");
|
|
78
|
+
var FlatTree = (function () {
|
|
79
|
+
function FlatTree(hyperplanes, offsets, children, indices) {
|
|
80
|
+
this.hyperplanes = hyperplanes;
|
|
81
|
+
this.offsets = offsets;
|
|
82
|
+
this.children = children;
|
|
83
|
+
this.indices = indices;
|
|
84
|
+
}
|
|
85
|
+
FlatTree.fromWasm = function (wasmTree) {
|
|
86
|
+
var jsData = (0, wasmBridge_1.wasmTreeToJs)(wasmTree);
|
|
87
|
+
var tree = new FlatTree(jsData.hyperplanes, jsData.offsets, jsData.children, jsData.indices);
|
|
88
|
+
tree.wasmTree = wasmTree;
|
|
89
|
+
return tree;
|
|
90
|
+
};
|
|
91
|
+
FlatTree.prototype.getWasmTree = function () {
|
|
92
|
+
return this.wasmTree;
|
|
93
|
+
};
|
|
94
|
+
FlatTree.prototype.dispose = function () {
|
|
95
|
+
if (this.wasmTree) {
|
|
96
|
+
this.wasmTree.free();
|
|
97
|
+
this.wasmTree = undefined;
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
return FlatTree;
|
|
101
|
+
}());
|
|
102
|
+
exports.FlatTree = FlatTree;
|
|
103
|
+
function makeForest(data, nNeighbors, nTrees, random, useWasm) {
|
|
104
|
+
if (useWasm === void 0) { useWasm = false; }
|
|
105
|
+
var leafSize = Math.max(10, nNeighbors);
|
|
106
|
+
if (useWasm) {
|
|
107
|
+
if (!(0, wasmBridge_1.isWasmAvailable)()) {
|
|
108
|
+
throw new Error('WASM requested but not available');
|
|
109
|
+
}
|
|
110
|
+
return makeForestWasm(data, leafSize, nTrees, random);
|
|
111
|
+
}
|
|
112
|
+
var trees = utils
|
|
113
|
+
.range(nTrees)
|
|
114
|
+
.map(function (_, i) { return makeTree(data, leafSize, i, random); });
|
|
115
|
+
var forest = trees.map(function (tree) { return flattenTree(tree, leafSize); });
|
|
116
|
+
return forest;
|
|
117
|
+
}
|
|
118
|
+
function makeForestWasm(data, leafSize, nTrees, random) {
|
|
119
|
+
var nSamples = data.length;
|
|
120
|
+
var dim = data[0].length;
|
|
121
|
+
var forest = [];
|
|
122
|
+
for (var i = 0; i < nTrees; i++) {
|
|
123
|
+
var seed = Math.floor(random() * 0xFFFFFFFF);
|
|
124
|
+
var wasmTree = (0, wasmBridge_1.buildRpTreeWasm)(data, nSamples, dim, leafSize, seed);
|
|
125
|
+
forest.push(FlatTree.fromWasm(wasmTree));
|
|
126
|
+
}
|
|
127
|
+
return forest;
|
|
128
|
+
}
|
|
129
|
+
function makeTree(data, leafSize, n, random) {
|
|
130
|
+
if (leafSize === void 0) { leafSize = 30; }
|
|
131
|
+
var indices = utils.range(data.length);
|
|
132
|
+
var tree = makeEuclideanTree(data, indices, leafSize, n, random);
|
|
133
|
+
return tree;
|
|
134
|
+
}
|
|
135
|
+
function makeEuclideanTree(data, indices, leafSize, q, random) {
|
|
136
|
+
if (leafSize === void 0) { leafSize = 30; }
|
|
137
|
+
if (indices.length > leafSize) {
|
|
138
|
+
var splitResults = euclideanRandomProjectionSplit(data, indices, random);
|
|
139
|
+
var indicesLeft = splitResults.indicesLeft, indicesRight = splitResults.indicesRight, hyperplane = splitResults.hyperplane, offset = splitResults.offset;
|
|
140
|
+
var leftChild = makeEuclideanTree(data, indicesLeft, leafSize, q + 1, random);
|
|
141
|
+
var rightChild = makeEuclideanTree(data, indicesRight, leafSize, q + 1, random);
|
|
142
|
+
var node = { leftChild: leftChild, rightChild: rightChild, isLeaf: false, hyperplane: hyperplane, offset: offset };
|
|
143
|
+
return node;
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
var node = { indices: indices, isLeaf: true };
|
|
147
|
+
return node;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
function euclideanRandomProjectionSplit(data, indices, random) {
|
|
151
|
+
var dim = data[0].length;
|
|
152
|
+
var leftIndex = utils.tauRandInt(indices.length, random);
|
|
153
|
+
var rightIndex = utils.tauRandInt(indices.length, random);
|
|
154
|
+
rightIndex += leftIndex === rightIndex ? 1 : 0;
|
|
155
|
+
rightIndex = rightIndex % indices.length;
|
|
156
|
+
var left = indices[leftIndex];
|
|
157
|
+
var right = indices[rightIndex];
|
|
158
|
+
var hyperplaneOffset = 0;
|
|
159
|
+
var hyperplaneVector = utils.zeros(dim);
|
|
160
|
+
for (var i = 0; i < hyperplaneVector.length; i++) {
|
|
161
|
+
hyperplaneVector[i] = data[left][i] - data[right][i];
|
|
162
|
+
hyperplaneOffset -=
|
|
163
|
+
(hyperplaneVector[i] * (data[left][i] + data[right][i])) / 2.0;
|
|
164
|
+
}
|
|
165
|
+
var nLeft = 0;
|
|
166
|
+
var nRight = 0;
|
|
167
|
+
var side = utils.zeros(indices.length);
|
|
168
|
+
for (var i = 0; i < indices.length; i++) {
|
|
169
|
+
var margin = hyperplaneOffset;
|
|
170
|
+
for (var d = 0; d < dim; d++) {
|
|
171
|
+
margin += hyperplaneVector[d] * data[indices[i]][d];
|
|
172
|
+
}
|
|
173
|
+
if (margin === 0) {
|
|
174
|
+
side[i] = utils.tauRandInt(2, random);
|
|
175
|
+
if (side[i] === 0) {
|
|
176
|
+
nLeft += 1;
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
nRight += 1;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
else if (margin > 0) {
|
|
183
|
+
side[i] = 0;
|
|
184
|
+
nLeft += 1;
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
side[i] = 1;
|
|
188
|
+
nRight += 1;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
var indicesLeft = utils.zeros(nLeft);
|
|
192
|
+
var indicesRight = utils.zeros(nRight);
|
|
193
|
+
nLeft = 0;
|
|
194
|
+
nRight = 0;
|
|
195
|
+
for (var i = 0; i < side.length; i++) {
|
|
196
|
+
if (side[i] === 0) {
|
|
197
|
+
indicesLeft[nLeft] = indices[i];
|
|
198
|
+
nLeft += 1;
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
indicesRight[nRight] = indices[i];
|
|
202
|
+
nRight += 1;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return {
|
|
206
|
+
indicesLeft: indicesLeft,
|
|
207
|
+
indicesRight: indicesRight,
|
|
208
|
+
hyperplane: hyperplaneVector,
|
|
209
|
+
offset: hyperplaneOffset,
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
function flattenTree(tree, leafSize) {
|
|
213
|
+
var nNodes = numNodes(tree);
|
|
214
|
+
var nLeaves = numLeaves(tree);
|
|
215
|
+
var hyperplanes = utils
|
|
216
|
+
.range(nNodes)
|
|
217
|
+
.map(function () { return utils.zeros(tree.hyperplane ? tree.hyperplane.length : 0); });
|
|
218
|
+
var offsets = utils.zeros(nNodes);
|
|
219
|
+
var children = utils.range(nNodes).map(function () { return [-1, -1]; });
|
|
220
|
+
var indices = utils
|
|
221
|
+
.range(nLeaves)
|
|
222
|
+
.map(function () { return utils.range(leafSize).map(function () { return -1; }); });
|
|
223
|
+
recursiveFlatten(tree, hyperplanes, offsets, children, indices, 0, 0);
|
|
224
|
+
return new FlatTree(hyperplanes, offsets, children, indices);
|
|
225
|
+
}
|
|
226
|
+
function recursiveFlatten(tree, hyperplanes, offsets, children, indices, nodeNum, leafNum) {
|
|
227
|
+
var _a;
|
|
228
|
+
if (tree.isLeaf) {
|
|
229
|
+
children[nodeNum][0] = -leafNum;
|
|
230
|
+
(_a = indices[leafNum]).splice.apply(_a, __spreadArray([0, tree.indices.length], __read(tree.indices), false));
|
|
231
|
+
leafNum += 1;
|
|
232
|
+
return { nodeNum: nodeNum, leafNum: leafNum };
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
hyperplanes[nodeNum] = tree.hyperplane;
|
|
236
|
+
offsets[nodeNum] = tree.offset;
|
|
237
|
+
children[nodeNum][0] = nodeNum + 1;
|
|
238
|
+
var oldNodeNum = nodeNum;
|
|
239
|
+
var res = recursiveFlatten(tree.leftChild, hyperplanes, offsets, children, indices, nodeNum + 1, leafNum);
|
|
240
|
+
nodeNum = res.nodeNum;
|
|
241
|
+
leafNum = res.leafNum;
|
|
242
|
+
children[oldNodeNum][1] = nodeNum + 1;
|
|
243
|
+
res = recursiveFlatten(tree.rightChild, hyperplanes, offsets, children, indices, nodeNum + 1, leafNum);
|
|
244
|
+
return { nodeNum: res.nodeNum, leafNum: res.leafNum };
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
function numNodes(tree) {
|
|
248
|
+
if (tree.isLeaf) {
|
|
249
|
+
return 1;
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
return 1 + numNodes(tree.leftChild) + numNodes(tree.rightChild);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
function numLeaves(tree) {
|
|
256
|
+
if (tree.isLeaf) {
|
|
257
|
+
return 1;
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
return numLeaves(tree.leftChild) + numLeaves(tree.rightChild);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
function makeLeafArray(rpForest) {
|
|
264
|
+
var e_1, _a;
|
|
265
|
+
if (rpForest.length > 0) {
|
|
266
|
+
var output = [];
|
|
267
|
+
try {
|
|
268
|
+
for (var rpForest_1 = __values(rpForest), rpForest_1_1 = rpForest_1.next(); !rpForest_1_1.done; rpForest_1_1 = rpForest_1.next()) {
|
|
269
|
+
var tree = rpForest_1_1.value;
|
|
270
|
+
output.push.apply(output, __spreadArray([], __read(tree.indices), false));
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
274
|
+
finally {
|
|
275
|
+
try {
|
|
276
|
+
if (rpForest_1_1 && !rpForest_1_1.done && (_a = rpForest_1.return)) _a.call(rpForest_1);
|
|
277
|
+
}
|
|
278
|
+
finally { if (e_1) throw e_1.error; }
|
|
279
|
+
}
|
|
280
|
+
return output;
|
|
281
|
+
}
|
|
282
|
+
else {
|
|
283
|
+
return [[-1]];
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
function selectSide(hyperplane, offset, point, random) {
|
|
287
|
+
var margin = offset;
|
|
288
|
+
for (var d = 0; d < point.length; d++) {
|
|
289
|
+
margin += hyperplane[d] * point[d];
|
|
290
|
+
}
|
|
291
|
+
if (margin === 0) {
|
|
292
|
+
var side = utils.tauRandInt(2, random);
|
|
293
|
+
return side;
|
|
294
|
+
}
|
|
295
|
+
else if (margin > 0) {
|
|
296
|
+
return 0;
|
|
297
|
+
}
|
|
298
|
+
else {
|
|
299
|
+
return 1;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
function searchFlatTree(point, tree, random) {
|
|
303
|
+
var wasmTree = tree.getWasmTree();
|
|
304
|
+
if (wasmTree && (0, wasmBridge_1.isWasmAvailable)()) {
|
|
305
|
+
var seed = Math.floor(random() * 0xFFFFFFFF);
|
|
306
|
+
return (0, wasmBridge_1.searchFlatTreeWasm)(wasmTree, point, seed);
|
|
307
|
+
}
|
|
308
|
+
var node = 0;
|
|
309
|
+
while (tree.children[node][0] > 0) {
|
|
310
|
+
var side = selectSide(tree.hyperplanes[node], tree.offsets[node], point, random);
|
|
311
|
+
if (side === 0) {
|
|
312
|
+
node = tree.children[node][0];
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
node = tree.children[node][1];
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
var index = -1 * tree.children[node][0];
|
|
319
|
+
return tree.indices[index];
|
|
320
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import * as matrix from './matrix';
|
|
2
|
+
export type DistanceFn = (x: Vector, y: Vector) => number;
|
|
3
|
+
export type RandomFn = () => number;
|
|
4
|
+
export type EpochCallback = (epoch: number) => boolean | void;
|
|
5
|
+
export type Vector = number[];
|
|
6
|
+
export type Vectors = Vector[];
|
|
7
|
+
export declare const enum TargetMetric {
|
|
8
|
+
categorical = "categorical",
|
|
9
|
+
l1 = "l1",
|
|
10
|
+
l2 = "l2"
|
|
11
|
+
}
|
|
12
|
+
export interface UMAPParameters {
|
|
13
|
+
distanceFn?: DistanceFn;
|
|
14
|
+
useWasmDistance?: boolean;
|
|
15
|
+
learningRate?: number;
|
|
16
|
+
localConnectivity?: number;
|
|
17
|
+
minDist?: number;
|
|
18
|
+
nComponents?: number;
|
|
19
|
+
nEpochs?: number;
|
|
20
|
+
nNeighbors?: number;
|
|
21
|
+
negativeSampleRate?: number;
|
|
22
|
+
repulsionStrength?: number;
|
|
23
|
+
random?: RandomFn;
|
|
24
|
+
setOpMixRatio?: number;
|
|
25
|
+
spread?: number;
|
|
26
|
+
transformQueueSize?: number;
|
|
27
|
+
}
|
|
28
|
+
export interface UMAPSupervisedParams {
|
|
29
|
+
targetMetric?: TargetMetric;
|
|
30
|
+
targetWeight?: number;
|
|
31
|
+
targetNNeighbors?: number;
|
|
32
|
+
}
|
|
33
|
+
export declare class UMAP {
|
|
34
|
+
private learningRate;
|
|
35
|
+
private localConnectivity;
|
|
36
|
+
private minDist;
|
|
37
|
+
private nComponents;
|
|
38
|
+
private nEpochs;
|
|
39
|
+
private nNeighbors;
|
|
40
|
+
private negativeSampleRate;
|
|
41
|
+
private random;
|
|
42
|
+
private repulsionStrength;
|
|
43
|
+
private setOpMixRatio;
|
|
44
|
+
private spread;
|
|
45
|
+
private transformQueueSize;
|
|
46
|
+
private targetMetric;
|
|
47
|
+
private targetWeight;
|
|
48
|
+
private targetNNeighbors;
|
|
49
|
+
private distanceFn;
|
|
50
|
+
private useWasmDistance;
|
|
51
|
+
private useWasmMatrix;
|
|
52
|
+
private useWasmTree;
|
|
53
|
+
private knnIndices?;
|
|
54
|
+
private knnDistances?;
|
|
55
|
+
private graph;
|
|
56
|
+
private X;
|
|
57
|
+
private isInitialized;
|
|
58
|
+
private rpForest;
|
|
59
|
+
private initFromRandom;
|
|
60
|
+
private initFromTree;
|
|
61
|
+
private search;
|
|
62
|
+
private searchGraph;
|
|
63
|
+
private Y?;
|
|
64
|
+
private embedding;
|
|
65
|
+
private optimizationState;
|
|
66
|
+
constructor(params?: UMAPParameters);
|
|
67
|
+
fit(X: Vectors): number[][];
|
|
68
|
+
fitAsync(X: Vectors, callback?: (epochNumber: number) => void | boolean): Promise<number[][]>;
|
|
69
|
+
setSupervisedProjection(Y: number[], params?: UMAPSupervisedParams): void;
|
|
70
|
+
setPrecomputedKNN(knnIndices: number[][], knnDistances: number[][]): void;
|
|
71
|
+
initializeFit(X: Vectors): number;
|
|
72
|
+
private makeSearchFns;
|
|
73
|
+
computeDistance(a: Vector, b: Vector): any;
|
|
74
|
+
private makeSearchGraph;
|
|
75
|
+
transform(toTransform: Vectors): Vectors;
|
|
76
|
+
private processGraphForSupervisedProjection;
|
|
77
|
+
step(): number;
|
|
78
|
+
getEmbedding(): number[][];
|
|
79
|
+
private nearestNeighbors;
|
|
80
|
+
private fuzzySimplicialSet;
|
|
81
|
+
private categoricalSimplicialSetIntersection;
|
|
82
|
+
private smoothKNNDistance;
|
|
83
|
+
private computeMembershipStrengths;
|
|
84
|
+
private initializeSimplicialSetEmbedding;
|
|
85
|
+
private makeEpochsPerSample;
|
|
86
|
+
private assignOptimizationStateParameters;
|
|
87
|
+
private prepareForOptimizationLoop;
|
|
88
|
+
private initializeOptimization;
|
|
89
|
+
private optimizeLayoutStep;
|
|
90
|
+
private optimizeLayoutAsync;
|
|
91
|
+
private optimizeLayout;
|
|
92
|
+
private getNEpochs;
|
|
93
|
+
}
|
|
94
|
+
export declare function euclidean(x: Vector, y: Vector): number;
|
|
95
|
+
export declare function cosine(x: Vector, y: Vector): number;
|
|
96
|
+
export declare function findABParams(spread: number, minDist: number): {
|
|
97
|
+
a: number;
|
|
98
|
+
b: number;
|
|
99
|
+
};
|
|
100
|
+
export declare function fastIntersection(graph: matrix.SparseMatrix, target: number[], unknownDist?: number, farDist?: number): matrix.SparseMatrix;
|
|
101
|
+
export declare function resetLocalConnectivity(simplicialSet: matrix.SparseMatrix): matrix.SparseMatrix;
|
|
102
|
+
export declare function initTransform(indices: number[][], weights: number[][], embedding: Vectors): number[][];
|