@elarsaks/umap-wasm 0.1.2 → 0.1.4

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.
Files changed (52) hide show
  1. package/dist/{src/heap.d.ts → heap.d.ts} +1 -1
  2. package/dist/heap.js +184 -0
  3. package/dist/index.d.ts +2 -0
  4. package/dist/index.js +2 -0
  5. package/dist/lib.d.ts +2 -0
  6. package/dist/lib.js +2 -0
  7. package/dist/matrix.js +257 -0
  8. package/dist/{src/nn_descent.d.ts → nn_descent.d.ts} +4 -4
  9. package/dist/nn_descent.js +127 -0
  10. package/dist/{src/tree.d.ts → tree.d.ts} +2 -2
  11. package/dist/tree.js +228 -0
  12. package/dist/{src/umap.d.ts → umap.d.ts} +1 -1
  13. package/dist/umap.js +700 -0
  14. package/dist/{src/utils.d.ts → utils.d.ts} +1 -1
  15. package/dist/utils.js +98 -0
  16. package/dist/wasmBridge.js +188 -0
  17. package/lib/umap-js.js +6842 -7490
  18. package/lib/umap-js.min.js +1 -1
  19. package/package.json +64 -63
  20. package/dist/src/heap.js +0 -226
  21. package/dist/src/index.d.ts +0 -2
  22. package/dist/src/index.js +0 -8
  23. package/dist/src/lib.d.ts +0 -1
  24. package/dist/src/lib.js +0 -5
  25. package/dist/src/matrix.js +0 -360
  26. package/dist/src/nn_descent.js +0 -204
  27. package/dist/src/tree.js +0 -320
  28. package/dist/src/umap.js +0 -842
  29. package/dist/src/utils.js +0 -137
  30. package/dist/src/wasmBridge.js +0 -290
  31. package/dist/test/matrix.test.d.ts +0 -1
  32. package/dist/test/matrix.test.js +0 -169
  33. package/dist/test/nn_descent.test.d.ts +0 -1
  34. package/dist/test/nn_descent.test.js +0 -58
  35. package/dist/test/smoke.playwright.test.d.ts +0 -1
  36. package/dist/test/smoke.playwright.test.js +0 -98
  37. package/dist/test/test_data.d.ts +0 -13
  38. package/dist/test/test_data.js +0 -1054
  39. package/dist/test/tree.test.d.ts +0 -1
  40. package/dist/test/tree.test.js +0 -60
  41. package/dist/test/umap.test.d.ts +0 -1
  42. package/dist/test/umap.test.js +0 -293
  43. package/dist/test/utils.test.d.ts +0 -1
  44. package/dist/test/utils.test.js +0 -128
  45. package/dist/test/wasmDistance.test.d.ts +0 -1
  46. package/dist/test/wasmDistance.test.js +0 -124
  47. package/dist/test/wasmMatrix.test.d.ts +0 -1
  48. package/dist/test/wasmMatrix.test.js +0 -389
  49. package/dist/test/wasmTree.test.d.ts +0 -1
  50. package/dist/test/wasmTree.test.js +0 -212
  51. /package/dist/{src/matrix.d.ts → matrix.d.ts} +0 -0
  52. /package/dist/{src/wasmBridge.d.ts → wasmBridge.d.ts} +0 -0
package/dist/tree.js ADDED
@@ -0,0 +1,228 @@
1
+ import * as utils from './utils.js';
2
+ import { isWasmAvailable, buildRpTreeWasm, searchFlatTreeWasm, wasmTreeToJs } from './wasmBridge.js';
3
+ export class FlatTree {
4
+ constructor(hyperplanes, offsets, children, indices) {
5
+ this.hyperplanes = hyperplanes;
6
+ this.offsets = offsets;
7
+ this.children = children;
8
+ this.indices = indices;
9
+ }
10
+ static fromWasm(wasmTree) {
11
+ const jsData = wasmTreeToJs(wasmTree);
12
+ const tree = new FlatTree(jsData.hyperplanes, jsData.offsets, jsData.children, jsData.indices);
13
+ tree.wasmTree = wasmTree;
14
+ return tree;
15
+ }
16
+ getWasmTree() {
17
+ return this.wasmTree;
18
+ }
19
+ dispose() {
20
+ if (this.wasmTree) {
21
+ this.wasmTree.free();
22
+ this.wasmTree = undefined;
23
+ }
24
+ }
25
+ }
26
+ export function makeForest(data, nNeighbors, nTrees, random, useWasm = false) {
27
+ const leafSize = Math.max(10, nNeighbors);
28
+ if (useWasm) {
29
+ if (!isWasmAvailable()) {
30
+ throw new Error('WASM requested but not available');
31
+ }
32
+ return makeForestWasm(data, leafSize, nTrees, random);
33
+ }
34
+ const trees = utils
35
+ .range(nTrees)
36
+ .map((_, i) => makeTree(data, leafSize, i, random));
37
+ const forest = trees.map(tree => flattenTree(tree, leafSize));
38
+ return forest;
39
+ }
40
+ function makeForestWasm(data, leafSize, nTrees, random) {
41
+ const nSamples = data.length;
42
+ const dim = data[0].length;
43
+ const forest = [];
44
+ for (let i = 0; i < nTrees; i++) {
45
+ const seed = Math.floor(random() * 0xFFFFFFFF);
46
+ const wasmTree = buildRpTreeWasm(data, nSamples, dim, leafSize, seed);
47
+ forest.push(FlatTree.fromWasm(wasmTree));
48
+ }
49
+ return forest;
50
+ }
51
+ function makeTree(data, leafSize = 30, n, random) {
52
+ const indices = utils.range(data.length);
53
+ const tree = makeEuclideanTree(data, indices, leafSize, n, random);
54
+ return tree;
55
+ }
56
+ function makeEuclideanTree(data, indices, leafSize = 30, q, random) {
57
+ if (indices.length > leafSize) {
58
+ const splitResults = euclideanRandomProjectionSplit(data, indices, random);
59
+ const { indicesLeft, indicesRight, hyperplane, offset } = splitResults;
60
+ const leftChild = makeEuclideanTree(data, indicesLeft, leafSize, q + 1, random);
61
+ const rightChild = makeEuclideanTree(data, indicesRight, leafSize, q + 1, random);
62
+ const node = { leftChild, rightChild, isLeaf: false, hyperplane, offset };
63
+ return node;
64
+ }
65
+ else {
66
+ const node = { indices, isLeaf: true };
67
+ return node;
68
+ }
69
+ }
70
+ function euclideanRandomProjectionSplit(data, indices, random) {
71
+ const dim = data[0].length;
72
+ let leftIndex = utils.tauRandInt(indices.length, random);
73
+ let rightIndex = utils.tauRandInt(indices.length, random);
74
+ rightIndex += leftIndex === rightIndex ? 1 : 0;
75
+ rightIndex = rightIndex % indices.length;
76
+ const left = indices[leftIndex];
77
+ const right = indices[rightIndex];
78
+ let hyperplaneOffset = 0;
79
+ const hyperplaneVector = utils.zeros(dim);
80
+ for (let i = 0; i < hyperplaneVector.length; i++) {
81
+ hyperplaneVector[i] = data[left][i] - data[right][i];
82
+ hyperplaneOffset -=
83
+ (hyperplaneVector[i] * (data[left][i] + data[right][i])) / 2.0;
84
+ }
85
+ let nLeft = 0;
86
+ let nRight = 0;
87
+ const side = utils.zeros(indices.length);
88
+ for (let i = 0; i < indices.length; i++) {
89
+ let margin = hyperplaneOffset;
90
+ for (let d = 0; d < dim; d++) {
91
+ margin += hyperplaneVector[d] * data[indices[i]][d];
92
+ }
93
+ if (margin === 0) {
94
+ side[i] = utils.tauRandInt(2, random);
95
+ if (side[i] === 0) {
96
+ nLeft += 1;
97
+ }
98
+ else {
99
+ nRight += 1;
100
+ }
101
+ }
102
+ else if (margin > 0) {
103
+ side[i] = 0;
104
+ nLeft += 1;
105
+ }
106
+ else {
107
+ side[i] = 1;
108
+ nRight += 1;
109
+ }
110
+ }
111
+ const indicesLeft = utils.zeros(nLeft);
112
+ const indicesRight = utils.zeros(nRight);
113
+ nLeft = 0;
114
+ nRight = 0;
115
+ for (let i = 0; i < side.length; i++) {
116
+ if (side[i] === 0) {
117
+ indicesLeft[nLeft] = indices[i];
118
+ nLeft += 1;
119
+ }
120
+ else {
121
+ indicesRight[nRight] = indices[i];
122
+ nRight += 1;
123
+ }
124
+ }
125
+ return {
126
+ indicesLeft,
127
+ indicesRight,
128
+ hyperplane: hyperplaneVector,
129
+ offset: hyperplaneOffset,
130
+ };
131
+ }
132
+ function flattenTree(tree, leafSize) {
133
+ const nNodes = numNodes(tree);
134
+ const nLeaves = numLeaves(tree);
135
+ const hyperplanes = utils
136
+ .range(nNodes)
137
+ .map(() => utils.zeros(tree.hyperplane ? tree.hyperplane.length : 0));
138
+ const offsets = utils.zeros(nNodes);
139
+ const children = utils.range(nNodes).map(() => [-1, -1]);
140
+ const indices = utils
141
+ .range(nLeaves)
142
+ .map(() => utils.range(leafSize).map(() => -1));
143
+ recursiveFlatten(tree, hyperplanes, offsets, children, indices, 0, 0);
144
+ return new FlatTree(hyperplanes, offsets, children, indices);
145
+ }
146
+ function recursiveFlatten(tree, hyperplanes, offsets, children, indices, nodeNum, leafNum) {
147
+ if (tree.isLeaf) {
148
+ children[nodeNum][0] = -leafNum;
149
+ indices[leafNum].splice(0, tree.indices.length, ...tree.indices);
150
+ leafNum += 1;
151
+ return { nodeNum, leafNum };
152
+ }
153
+ else {
154
+ hyperplanes[nodeNum] = tree.hyperplane;
155
+ offsets[nodeNum] = tree.offset;
156
+ children[nodeNum][0] = nodeNum + 1;
157
+ const oldNodeNum = nodeNum;
158
+ let res = recursiveFlatten(tree.leftChild, hyperplanes, offsets, children, indices, nodeNum + 1, leafNum);
159
+ nodeNum = res.nodeNum;
160
+ leafNum = res.leafNum;
161
+ children[oldNodeNum][1] = nodeNum + 1;
162
+ res = recursiveFlatten(tree.rightChild, hyperplanes, offsets, children, indices, nodeNum + 1, leafNum);
163
+ return { nodeNum: res.nodeNum, leafNum: res.leafNum };
164
+ }
165
+ }
166
+ function numNodes(tree) {
167
+ if (tree.isLeaf) {
168
+ return 1;
169
+ }
170
+ else {
171
+ return 1 + numNodes(tree.leftChild) + numNodes(tree.rightChild);
172
+ }
173
+ }
174
+ function numLeaves(tree) {
175
+ if (tree.isLeaf) {
176
+ return 1;
177
+ }
178
+ else {
179
+ return numLeaves(tree.leftChild) + numLeaves(tree.rightChild);
180
+ }
181
+ }
182
+ export function makeLeafArray(rpForest) {
183
+ if (rpForest.length > 0) {
184
+ const output = [];
185
+ for (let tree of rpForest) {
186
+ output.push(...tree.indices);
187
+ }
188
+ return output;
189
+ }
190
+ else {
191
+ return [[-1]];
192
+ }
193
+ }
194
+ function selectSide(hyperplane, offset, point, random) {
195
+ let margin = offset;
196
+ for (let d = 0; d < point.length; d++) {
197
+ margin += hyperplane[d] * point[d];
198
+ }
199
+ if (margin === 0) {
200
+ const side = utils.tauRandInt(2, random);
201
+ return side;
202
+ }
203
+ else if (margin > 0) {
204
+ return 0;
205
+ }
206
+ else {
207
+ return 1;
208
+ }
209
+ }
210
+ export function searchFlatTree(point, tree, random) {
211
+ const wasmTree = tree.getWasmTree();
212
+ if (wasmTree && isWasmAvailable()) {
213
+ const seed = Math.floor(random() * 0xFFFFFFFF);
214
+ return searchFlatTreeWasm(wasmTree, point, seed);
215
+ }
216
+ let node = 0;
217
+ while (tree.children[node][0] > 0) {
218
+ const side = selectSide(tree.hyperplanes[node], tree.offsets[node], point, random);
219
+ if (side === 0) {
220
+ node = tree.children[node][0];
221
+ }
222
+ else {
223
+ node = tree.children[node][1];
224
+ }
225
+ }
226
+ const index = -1 * tree.children[node][0];
227
+ return tree.indices[index];
228
+ }
@@ -1,4 +1,4 @@
1
- import * as matrix from './matrix';
1
+ import * as matrix from './matrix.js';
2
2
  export type DistanceFn = (x: Vector, y: Vector) => number;
3
3
  export type RandomFn = () => number;
4
4
  export type EpochCallback = (epoch: number) => boolean | void;