@graphty/algorithms 1.1.0 → 1.2.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/README.md +464 -33
- package/dist/src/clustering/spectral.d.ts.map +1 -1
- package/dist/src/clustering/spectral.js +123 -19
- package/dist/src/clustering/spectral.js.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/research/grsbm.d.ts +83 -0
- package/dist/src/research/grsbm.d.ts.map +1 -0
- package/dist/src/research/grsbm.js +411 -0
- package/dist/src/research/grsbm.js.map +1 -0
- package/dist/src/research/index.d.ts +12 -0
- package/dist/src/research/index.d.ts.map +1 -0
- package/dist/src/research/index.js +14 -0
- package/dist/src/research/index.js.map +1 -0
- package/dist/src/research/sync.d.ts +49 -0
- package/dist/src/research/sync.d.ts.map +1 -0
- package/dist/src/research/sync.js +342 -0
- package/dist/src/research/sync.js.map +1 -0
- package/dist/src/research/terahac.d.ts +63 -0
- package/dist/src/research/terahac.d.ts.map +1 -0
- package/dist/src/research/terahac.js +375 -0
- package/dist/src/research/terahac.js.map +1 -0
- package/package.json +11 -2
- package/src/clustering/spectral.ts +147 -20
- package/src/index.ts +3 -0
- package/src/research/grsbm.ts +589 -0
- package/src/research/index.ts +17 -0
- package/src/research/sync.ts +469 -0
- package/src/research/terahac.ts +506 -0
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TeraHAC - Hierarchical Agglomerative Clustering for Large Graphs
|
|
3
|
+
*
|
|
4
|
+
* This algorithm performs hierarchical clustering on graphs by iteratively
|
|
5
|
+
* merging the closest clusters. Optimized for scalability to handle large
|
|
6
|
+
* graphs efficiently.
|
|
7
|
+
*
|
|
8
|
+
* Based on: "Scaling Hierarchical Agglomerative Clustering to Trillion-Edge Graphs"
|
|
9
|
+
* Google Research 2024
|
|
10
|
+
*
|
|
11
|
+
* @param graph - Input graph to cluster
|
|
12
|
+
* @param config - Configuration options
|
|
13
|
+
* @returns Hierarchical clustering result
|
|
14
|
+
*/
|
|
15
|
+
export function teraHAC(graph, config = {}) {
|
|
16
|
+
const { linkage = "average", numClusters, distanceThreshold, maxNodes = 10000, useGraphDistance = true, } = config;
|
|
17
|
+
const nodes = Array.from(graph.nodes());
|
|
18
|
+
const nodeCount = nodes.length;
|
|
19
|
+
if (nodeCount === 0) {
|
|
20
|
+
throw new Error("Cannot cluster empty graph");
|
|
21
|
+
}
|
|
22
|
+
if (nodeCount > maxNodes) {
|
|
23
|
+
console.warn(`Graph has ${String(nodeCount)} nodes, which exceeds maxNodes (${String(maxNodes)}). Performance may be degraded.`);
|
|
24
|
+
}
|
|
25
|
+
// Initialize each node as its own cluster
|
|
26
|
+
const clusters = new Map();
|
|
27
|
+
let nextClusterId = nodeCount;
|
|
28
|
+
for (let i = 0; i < nodeCount; i++) {
|
|
29
|
+
const node = nodes[i];
|
|
30
|
+
if (!node) {
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
const clusterId = i.toString();
|
|
34
|
+
clusters.set(clusterId, {
|
|
35
|
+
id: clusterId,
|
|
36
|
+
members: new Set([node.id]),
|
|
37
|
+
distance: 0,
|
|
38
|
+
size: 1,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
// Calculate initial distance matrix
|
|
42
|
+
const distanceMatrix = calculateDistanceMatrix(graph, nodes, useGraphDistance);
|
|
43
|
+
const mergeDistances = [];
|
|
44
|
+
// Priority queue for efficient nearest neighbor finding
|
|
45
|
+
const mergeCandidates = initializeMergeCandidates(clusters, distanceMatrix);
|
|
46
|
+
let dendrogram;
|
|
47
|
+
// Perform agglomerative clustering
|
|
48
|
+
while (clusters.size > 1) {
|
|
49
|
+
// Find closest pair of clusters
|
|
50
|
+
const { cluster1Id, cluster2Id, distance } = findClosestPair(mergeCandidates);
|
|
51
|
+
// Check stopping criteria
|
|
52
|
+
if (numClusters && clusters.size <= numClusters) {
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
if (distanceThreshold && distance > distanceThreshold) {
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
// Get the two clusters to merge
|
|
59
|
+
const cluster1 = clusters.get(cluster1Id);
|
|
60
|
+
const cluster2 = clusters.get(cluster2Id);
|
|
61
|
+
if (!cluster1 || !cluster2) {
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
// Create new merged cluster
|
|
65
|
+
const newClusterId = (nextClusterId++).toString();
|
|
66
|
+
const mergedMembers = new Set([...cluster1.members, ...cluster2.members]);
|
|
67
|
+
const newCluster = {
|
|
68
|
+
id: newClusterId,
|
|
69
|
+
members: mergedMembers,
|
|
70
|
+
left: cluster1,
|
|
71
|
+
right: cluster2,
|
|
72
|
+
distance,
|
|
73
|
+
size: cluster1.size + cluster2.size,
|
|
74
|
+
};
|
|
75
|
+
// Remove old clusters
|
|
76
|
+
clusters.delete(cluster1Id);
|
|
77
|
+
clusters.delete(cluster2Id);
|
|
78
|
+
// Add new cluster
|
|
79
|
+
clusters.set(newClusterId, newCluster);
|
|
80
|
+
mergeDistances.push(distance);
|
|
81
|
+
// Update merge candidates
|
|
82
|
+
updateMergeCandidates(mergeCandidates, cluster1Id, cluster2Id, newClusterId, clusters, distanceMatrix, linkage);
|
|
83
|
+
dendrogram = newCluster;
|
|
84
|
+
}
|
|
85
|
+
// If we have multiple remaining clusters, create a virtual root
|
|
86
|
+
if (clusters.size > 1) {
|
|
87
|
+
const remainingClusters = Array.from(clusters.values());
|
|
88
|
+
let root = remainingClusters[0];
|
|
89
|
+
if (!root) {
|
|
90
|
+
dendrogram = undefined;
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
for (let i = 1; i < remainingClusters.length; i++) {
|
|
94
|
+
const currentCluster = remainingClusters[i];
|
|
95
|
+
if (!currentCluster) {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
const newRoot = {
|
|
99
|
+
id: (nextClusterId++).toString(),
|
|
100
|
+
members: new Set([...root.members, ...currentCluster.members]),
|
|
101
|
+
left: root,
|
|
102
|
+
right: currentCluster,
|
|
103
|
+
distance: Infinity,
|
|
104
|
+
size: root.size + currentCluster.size,
|
|
105
|
+
};
|
|
106
|
+
root = newRoot;
|
|
107
|
+
}
|
|
108
|
+
dendrogram = root;
|
|
109
|
+
}
|
|
110
|
+
dendrogram = root;
|
|
111
|
+
}
|
|
112
|
+
dendrogram ?? (dendrogram = Array.from(clusters.values())[0]);
|
|
113
|
+
// Extract flat clustering
|
|
114
|
+
const finalNumClusters = numClusters ?? clusters.size;
|
|
115
|
+
const flatClusters = dendrogram ? extractFlatClustering(dendrogram, finalNumClusters) : new Map();
|
|
116
|
+
if (!dendrogram) {
|
|
117
|
+
throw new Error("Failed to create dendrogram");
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
dendrogram,
|
|
121
|
+
clusters: flatClusters,
|
|
122
|
+
distances: mergeDistances,
|
|
123
|
+
numClusters: finalNumClusters,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Calculate distance matrix between all pairs of nodes
|
|
128
|
+
*/
|
|
129
|
+
function calculateDistanceMatrix(graph, nodes, useGraphDistance) {
|
|
130
|
+
const n = nodes.length;
|
|
131
|
+
const matrix = Array.from({ length: n }, () => new Array(n).fill(Infinity));
|
|
132
|
+
if (useGraphDistance) {
|
|
133
|
+
// Use graph-based distances (shortest path)
|
|
134
|
+
for (let i = 0; i < n; i++) {
|
|
135
|
+
const node = nodes[i];
|
|
136
|
+
if (!node) {
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
const distances = bfsShortestPaths(graph, node.id);
|
|
140
|
+
for (let j = 0; j < n; j++) {
|
|
141
|
+
if (i !== j) {
|
|
142
|
+
const targetNode = nodes[j];
|
|
143
|
+
if (targetNode && i < matrix.length && j < n) {
|
|
144
|
+
const row = matrix[i];
|
|
145
|
+
if (row && j < row.length) {
|
|
146
|
+
const distance = distances.get(targetNode.id);
|
|
147
|
+
row[j] = distance ?? Infinity;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
else if (i < matrix.length) {
|
|
152
|
+
const row = matrix[i];
|
|
153
|
+
if (row && j < row.length) {
|
|
154
|
+
row[j] = 0;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
// Use simple edge-based distances
|
|
162
|
+
for (let i = 0; i < n; i++) {
|
|
163
|
+
for (let j = i + 1; j < n; j++) {
|
|
164
|
+
const node1 = nodes[i];
|
|
165
|
+
const node2 = nodes[j];
|
|
166
|
+
if (node1 && node2) {
|
|
167
|
+
const hasEdge = graph.hasEdge(node1.id, node2.id);
|
|
168
|
+
const distance = hasEdge ? 1 : 2; // Connected: 1, not connected: 2
|
|
169
|
+
if (i < matrix.length) {
|
|
170
|
+
const rowI = matrix[i];
|
|
171
|
+
if (rowI && j < rowI.length) {
|
|
172
|
+
rowI[j] = distance;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
if (j < matrix.length) {
|
|
176
|
+
const rowJ = matrix[j];
|
|
177
|
+
if (rowJ && i < rowJ.length) {
|
|
178
|
+
rowJ[i] = distance;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
if (i < matrix.length) {
|
|
184
|
+
const row = matrix[i];
|
|
185
|
+
if (row && i < row.length) {
|
|
186
|
+
row[i] = 0;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return matrix;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* BFS-based shortest path calculation from a source node
|
|
195
|
+
*/
|
|
196
|
+
function bfsShortestPaths(graph, source) {
|
|
197
|
+
const distances = new Map();
|
|
198
|
+
const queue = [[source, 0]];
|
|
199
|
+
const visited = new Set();
|
|
200
|
+
visited.add(source);
|
|
201
|
+
distances.set(source, 0);
|
|
202
|
+
while (queue.length > 0) {
|
|
203
|
+
const item = queue.shift();
|
|
204
|
+
if (!item) {
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
const [current, distance] = item;
|
|
208
|
+
for (const neighbor of graph.neighbors(current)) {
|
|
209
|
+
if (!visited.has(neighbor)) {
|
|
210
|
+
visited.add(neighbor);
|
|
211
|
+
distances.set(neighbor, distance + 1);
|
|
212
|
+
queue.push([neighbor, distance + 1]);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return distances;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Initialize merge candidates priority queue
|
|
220
|
+
*/
|
|
221
|
+
function initializeMergeCandidates(clusters, distanceMatrix) {
|
|
222
|
+
const candidates = [];
|
|
223
|
+
const clusterIds = Array.from(clusters.keys());
|
|
224
|
+
for (let i = 0; i < clusterIds.length; i++) {
|
|
225
|
+
for (let j = i + 1; j < clusterIds.length; j++) {
|
|
226
|
+
const id1 = clusterIds[i];
|
|
227
|
+
const id2 = clusterIds[j];
|
|
228
|
+
if (!id1 || !id2) {
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
const row = distanceMatrix[parseInt(id1)];
|
|
232
|
+
if (!row) {
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
const distance = row[parseInt(id2)] ?? Infinity;
|
|
236
|
+
// Include all candidates, even disconnected ones (with finite but large distance)
|
|
237
|
+
candidates.push({
|
|
238
|
+
cluster1Id: id1,
|
|
239
|
+
cluster2Id: id2,
|
|
240
|
+
distance: distance === Infinity ? 100 : distance,
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
// Sort by distance (ascending)
|
|
245
|
+
candidates.sort((a, b) => a.distance - b.distance);
|
|
246
|
+
return candidates;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Find the closest pair of clusters
|
|
250
|
+
*/
|
|
251
|
+
function findClosestPair(mergeCandidates) {
|
|
252
|
+
// Return the first (closest) valid candidate
|
|
253
|
+
const candidate = mergeCandidates.shift();
|
|
254
|
+
if (!candidate) {
|
|
255
|
+
throw new Error("No merge candidates available");
|
|
256
|
+
}
|
|
257
|
+
return candidate;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Update merge candidates after a merge operation
|
|
261
|
+
*/
|
|
262
|
+
function updateMergeCandidates(mergeCandidates, oldCluster1Id, oldCluster2Id, newClusterId, clusters, distanceMatrix, linkage) {
|
|
263
|
+
// Remove candidates involving the merged clusters
|
|
264
|
+
for (let i = mergeCandidates.length - 1; i >= 0; i--) {
|
|
265
|
+
const candidate = mergeCandidates[i];
|
|
266
|
+
if (!candidate) {
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
if (candidate.cluster1Id === oldCluster1Id || candidate.cluster1Id === oldCluster2Id ||
|
|
270
|
+
candidate.cluster2Id === oldCluster1Id || candidate.cluster2Id === oldCluster2Id) {
|
|
271
|
+
mergeCandidates.splice(i, 1);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
// Add new candidates for the merged cluster
|
|
275
|
+
const newCluster = clusters.get(newClusterId);
|
|
276
|
+
if (!newCluster) {
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
for (const [clusterId, cluster] of clusters) {
|
|
280
|
+
if (clusterId !== newClusterId) {
|
|
281
|
+
const distance = calculateClusterDistance(newCluster, cluster, distanceMatrix, linkage);
|
|
282
|
+
mergeCandidates.push({
|
|
283
|
+
cluster1Id: newClusterId,
|
|
284
|
+
cluster2Id: clusterId,
|
|
285
|
+
distance: distance === Infinity ? 100 : distance,
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
// Re-sort candidates
|
|
290
|
+
mergeCandidates.sort((a, b) => a.distance - b.distance);
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Calculate distance between two clusters based on linkage criterion
|
|
294
|
+
*/
|
|
295
|
+
function calculateClusterDistance(cluster1, cluster2, distanceMatrix, linkage) {
|
|
296
|
+
const members1 = Array.from(cluster1.members);
|
|
297
|
+
const members2 = Array.from(cluster2.members);
|
|
298
|
+
const distances = [];
|
|
299
|
+
// Calculate all pairwise distances between cluster members
|
|
300
|
+
for (const member1 of members1) {
|
|
301
|
+
for (const member2 of members2) {
|
|
302
|
+
const idx1 = parseInt(member1.toString());
|
|
303
|
+
const idx2 = parseInt(member2.toString());
|
|
304
|
+
if (idx1 < distanceMatrix.length && idx2 < distanceMatrix.length) {
|
|
305
|
+
const row = distanceMatrix[idx1];
|
|
306
|
+
if (row) {
|
|
307
|
+
const distance = row[idx2];
|
|
308
|
+
if (distance !== undefined) {
|
|
309
|
+
distances.push(distance);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
if (distances.length === 0) {
|
|
316
|
+
return Infinity;
|
|
317
|
+
}
|
|
318
|
+
// Apply linkage criterion
|
|
319
|
+
switch (linkage) {
|
|
320
|
+
case "single":
|
|
321
|
+
return Math.min(...distances);
|
|
322
|
+
case "complete":
|
|
323
|
+
return Math.max(...distances);
|
|
324
|
+
case "average":
|
|
325
|
+
return distances.reduce((sum, d) => sum + d, 0) / distances.length;
|
|
326
|
+
case "ward":
|
|
327
|
+
// Simplified Ward linkage (would need cluster centroids for full implementation)
|
|
328
|
+
return distances.reduce((sum, d) => sum + (d * d), 0) / distances.length;
|
|
329
|
+
default:
|
|
330
|
+
return distances.reduce((sum, d) => sum + d, 0) / distances.length;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Extract flat clustering from dendrogram
|
|
335
|
+
*/
|
|
336
|
+
function extractFlatClustering(dendrogram, numClusters) {
|
|
337
|
+
const clusters = new Map();
|
|
338
|
+
if (numClusters === 1) {
|
|
339
|
+
// Single cluster
|
|
340
|
+
const clusterId = 0;
|
|
341
|
+
for (const member of dendrogram.members) {
|
|
342
|
+
clusters.set(member, clusterId);
|
|
343
|
+
}
|
|
344
|
+
return clusters;
|
|
345
|
+
}
|
|
346
|
+
// Find clusters at the specified level
|
|
347
|
+
const clusterNodes = [];
|
|
348
|
+
const queue = [dendrogram];
|
|
349
|
+
while (queue.length > 0 && clusterNodes.length < numClusters) {
|
|
350
|
+
const current = queue.shift();
|
|
351
|
+
if (!current) {
|
|
352
|
+
break;
|
|
353
|
+
}
|
|
354
|
+
if (!current.left || !current.right || clusterNodes.length + queue.length + 1 >= numClusters) {
|
|
355
|
+
// This is a leaf or we need to keep this level
|
|
356
|
+
clusterNodes.push(current);
|
|
357
|
+
}
|
|
358
|
+
else {
|
|
359
|
+
// Continue decomposing
|
|
360
|
+
queue.push(current.left, current.right);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
// Assign cluster IDs
|
|
364
|
+
for (let i = 0; i < clusterNodes.length; i++) {
|
|
365
|
+
const cluster = clusterNodes[i];
|
|
366
|
+
if (!cluster) {
|
|
367
|
+
continue;
|
|
368
|
+
}
|
|
369
|
+
for (const member of cluster.members) {
|
|
370
|
+
clusters.set(member, i);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
return clusters;
|
|
374
|
+
}
|
|
375
|
+
//# sourceMappingURL=terahac.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terahac.js","sourceRoot":"","sources":["../../../src/research/terahac.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@graphty/algorithms",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Graph algorithms library for browser environments implemented in TypeScript",
|
|
5
5
|
"author": "Adam Powers <apowers@ato.ms>",
|
|
6
6
|
"main": "dist/algorithms.js",
|
|
@@ -35,10 +35,18 @@
|
|
|
35
35
|
"typecheck": "tsc --noEmit",
|
|
36
36
|
"prepare": "husky",
|
|
37
37
|
"build": "tsc",
|
|
38
|
+
"build:bundle": "node scripts/build-bundle.js",
|
|
39
|
+
"build:all": "npm run build && npm run build:bundle",
|
|
40
|
+
"build:gh-pages": "npm run build:bundle && node scripts/build-gh-pages.js",
|
|
38
41
|
"build:watch": "tsc --watch",
|
|
39
42
|
"dev": "tsc --watch",
|
|
43
|
+
"examples": "node examples/run-all-examples.js",
|
|
44
|
+
"examples:run": "node examples/run-all-examples.js",
|
|
45
|
+
"examples:html": "npm run build:bundle && vite",
|
|
46
|
+
"serve": "npm run build:bundle && vite",
|
|
40
47
|
"commit": "cz",
|
|
41
|
-
"ready:commit": "npm run build && npm run lint && npm run test:all"
|
|
48
|
+
"ready:commit": "npm run build && npm run lint && npm run test:all",
|
|
49
|
+
"watch": "tsc --watch"
|
|
42
50
|
},
|
|
43
51
|
"repository": {
|
|
44
52
|
"type": "git",
|
|
@@ -97,6 +105,7 @@
|
|
|
97
105
|
"tsx": "^4.20.3",
|
|
98
106
|
"typescript": "^5.8.3",
|
|
99
107
|
"typescript-eslint": "^8.34.1",
|
|
108
|
+
"vite": "^7.0.5",
|
|
100
109
|
"vitest": "^3.2.4"
|
|
101
110
|
}
|
|
102
111
|
}
|
|
@@ -59,13 +59,27 @@ export function spectralClustering(
|
|
|
59
59
|
// Find k smallest eigenvectors
|
|
60
60
|
const eigenResult = findSmallestEigenvectors(laplacianMatrix, k);
|
|
61
61
|
|
|
62
|
-
//
|
|
62
|
+
// Perform k-means clustering on the eigenvectors
|
|
63
|
+
// For spectral clustering, we need to transpose the eigenvector matrix
|
|
64
|
+
// Each row should be a data point (node) with features from the eigenvectors
|
|
65
|
+
const dataPoints: number[][] = [];
|
|
66
|
+
for (let i = 0; i < nodeIds.length; i++) {
|
|
67
|
+
const point: number[] = [];
|
|
68
|
+
for (let j = 0; j < k; j++) {
|
|
69
|
+
const eigenvector = eigenResult.eigenvectors[j];
|
|
70
|
+
if (eigenvector) {
|
|
71
|
+
point.push(eigenvector[i] ?? 0);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
dataPoints.push(point);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Normalize the data points row-wise (for normalized spectral clustering)
|
|
63
78
|
if (laplacianType === "normalized") {
|
|
64
|
-
normalizeRows(
|
|
79
|
+
normalizeRows(dataPoints);
|
|
65
80
|
}
|
|
66
81
|
|
|
67
|
-
|
|
68
|
-
const kmeans = kMeansClustering(eigenResult.eigenvectors, k, maxIterations);
|
|
82
|
+
const kmeans = kMeansClustering(dataPoints, k, maxIterations);
|
|
69
83
|
|
|
70
84
|
// Build communities
|
|
71
85
|
const communities: NodeId[][] = Array.from({length: k}, () => []);
|
|
@@ -265,32 +279,34 @@ function findSmallestEigenvectors(matrix: number[][], k: number): {
|
|
|
265
279
|
return {eigenvalues: [], eigenvectors: []};
|
|
266
280
|
}
|
|
267
281
|
|
|
268
|
-
// For small matrices
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
for (let i = 0; i < Math.min(k, n); i++) {
|
|
275
|
-
const vector = Array(n).fill(0) as number[];
|
|
276
|
-
vector[i] = 1;
|
|
277
|
-
eigenvectors.push(vector);
|
|
278
|
-
eigenvalues.push(i);
|
|
279
|
-
}
|
|
282
|
+
// For very small matrices, use the full power iteration approach
|
|
283
|
+
// Remove the simplified approach that was causing issues
|
|
284
|
+
if (k >= n) {
|
|
285
|
+
// If k >= n, we still need proper eigenvectors, not identity
|
|
286
|
+
// Fall through to the power iteration below
|
|
287
|
+
}
|
|
280
288
|
|
|
281
|
-
|
|
289
|
+
// For spectral clustering, we need proper eigenvectors
|
|
290
|
+
// Special handling for small k values which are common in clustering
|
|
291
|
+
if (k <= 3 && n > k) {
|
|
292
|
+
return computeSmallestEigenvectorsSimple(matrix, k, n);
|
|
282
293
|
}
|
|
283
294
|
|
|
284
|
-
// For larger
|
|
285
|
-
// This is still simplified but better than random
|
|
295
|
+
// For larger k, use power iteration
|
|
286
296
|
const eigenvectors: number[][] = [];
|
|
287
297
|
const eigenvalues: number[] = [];
|
|
288
|
-
const maxIterations =
|
|
298
|
+
const maxIterations = 100;
|
|
289
299
|
|
|
290
300
|
for (let eigIdx = 0; eigIdx < k; eigIdx++) {
|
|
291
301
|
// Initialize random vector
|
|
292
302
|
let vector = Array(n).fill(0).map(() => Math.random() - 0.5);
|
|
293
303
|
|
|
304
|
+
// Normalize initial vector
|
|
305
|
+
const initNorm = Math.sqrt(vector.reduce((sum, val) => sum + (val * val), 0));
|
|
306
|
+
if (initNorm > 0) {
|
|
307
|
+
vector = vector.map((val) => val / initNorm);
|
|
308
|
+
}
|
|
309
|
+
|
|
294
310
|
// Orthogonalize against previous eigenvectors
|
|
295
311
|
for (let j = 0; j < eigIdx; j++) {
|
|
296
312
|
const ejVector = eigenvectors[j];
|
|
@@ -543,3 +559,114 @@ function euclideanDistance(a: number[], b: number[]): number {
|
|
|
543
559
|
}
|
|
544
560
|
return Math.sqrt(sum);
|
|
545
561
|
}
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* Compute smallest eigenvectors for small k (optimized for k=2, k=3)
|
|
565
|
+
*/
|
|
566
|
+
function computeSmallestEigenvectorsSimple(matrix: number[][], k: number, n: number): {
|
|
567
|
+
eigenvalues: number[];
|
|
568
|
+
eigenvectors: number[][];
|
|
569
|
+
} {
|
|
570
|
+
const eigenvectors: number[][] = [];
|
|
571
|
+
const eigenvalues: number[] = [];
|
|
572
|
+
|
|
573
|
+
// First eigenvector is constant (corresponds to eigenvalue 0 for connected graph)
|
|
574
|
+
const firstVector = Array(n).fill(1 / Math.sqrt(n)) as number[];
|
|
575
|
+
eigenvectors.push(firstVector);
|
|
576
|
+
eigenvalues.push(0);
|
|
577
|
+
|
|
578
|
+
// For k >= 2, compute the Fiedler vector (second smallest eigenvector)
|
|
579
|
+
if (k >= 2) {
|
|
580
|
+
// Use power iteration on I - L/lambda_max to find second smallest
|
|
581
|
+
const maxEig = 2; // For normalized Laplacian, max eigenvalue <= 2
|
|
582
|
+
let vector = Array(n).fill(0).map(() => Math.random() - 0.5);
|
|
583
|
+
|
|
584
|
+
// Make orthogonal to first eigenvector
|
|
585
|
+
const dot1 = vector.reduce((sum, val) => sum + (val / Math.sqrt(n)), 0);
|
|
586
|
+
vector = vector.map((val) => val - (dot1 / Math.sqrt(n)));
|
|
587
|
+
|
|
588
|
+
// Power iteration on shifted matrix
|
|
589
|
+
for (let iter = 0; iter < 100; iter++) {
|
|
590
|
+
// Compute (I - L/maxEig) * v
|
|
591
|
+
const newVector = Array(n).fill(0) as number[];
|
|
592
|
+
|
|
593
|
+
// Identity part
|
|
594
|
+
for (let i = 0; i < n; i++) {
|
|
595
|
+
newVector[i] = vector[i] ?? 0;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
// Subtract L * v / maxEig
|
|
599
|
+
for (let i = 0; i < n; i++) {
|
|
600
|
+
for (let j = 0; j < n; j++) {
|
|
601
|
+
const matrixVal = matrix[i]?.[j] ?? 0;
|
|
602
|
+
const vecVal = vector[j] ?? 0;
|
|
603
|
+
newVector[i] = (newVector[i] ?? 0) - ((matrixVal * vecVal) / maxEig);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// Orthogonalize against first eigenvector
|
|
608
|
+
const dot = newVector.reduce((sum, val) => sum + (val / Math.sqrt(n)), 0);
|
|
609
|
+
for (let i = 0; i < n; i++) {
|
|
610
|
+
newVector[i] = (newVector[i] ?? 0) - (dot / Math.sqrt(n));
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
// Normalize
|
|
614
|
+
const norm = Math.sqrt(newVector.reduce((sum, val) => sum + (val * val), 0));
|
|
615
|
+
if (norm > 1e-10) {
|
|
616
|
+
vector = newVector.map((val) => val / norm);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
eigenvectors.push(vector);
|
|
621
|
+
eigenvalues.push(0.1); // Approximate
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
// For k = 3, add another eigenvector
|
|
625
|
+
if (k >= 3) {
|
|
626
|
+
let vector = Array(n).fill(0).map(() => Math.random() - 0.5);
|
|
627
|
+
|
|
628
|
+
// Orthogonalize against previous eigenvectors
|
|
629
|
+
for (const prev of eigenvectors) {
|
|
630
|
+
const dot = vector.reduce((sum, val, idx) => sum + (val * (prev[idx] ?? 0)), 0);
|
|
631
|
+
vector = vector.map((val, idx) => val - (dot * (prev[idx] ?? 0)));
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
// Similar power iteration
|
|
635
|
+
for (let iter = 0; iter < 50; iter++) {
|
|
636
|
+
const newVector = Array(n).fill(0) as number[];
|
|
637
|
+
|
|
638
|
+
// Identity part
|
|
639
|
+
for (let i = 0; i < n; i++) {
|
|
640
|
+
newVector[i] = vector[i] ?? 0;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
// Subtract L * v / 2
|
|
644
|
+
for (let i = 0; i < n; i++) {
|
|
645
|
+
for (let j = 0; j < n; j++) {
|
|
646
|
+
const matrixVal = matrix[i]?.[j] ?? 0;
|
|
647
|
+
const vecVal = vector[j] ?? 0;
|
|
648
|
+
newVector[i] = (newVector[i] ?? 0) - ((matrixVal * vecVal) / 2);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
// Orthogonalize
|
|
653
|
+
for (const prev of eigenvectors) {
|
|
654
|
+
const dot = newVector.reduce((sum, val, idx) => sum + (val * (prev[idx] ?? 0)), 0);
|
|
655
|
+
for (let i = 0; i < n; i++) {
|
|
656
|
+
newVector[i] = (newVector[i] ?? 0) - (dot * (prev[i] ?? 0));
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
// Normalize
|
|
661
|
+
const norm = Math.sqrt(newVector.reduce((sum, val) => sum + (val * val), 0));
|
|
662
|
+
if (norm > 1e-10) {
|
|
663
|
+
vector = newVector.map((val) => val / norm);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
eigenvectors.push(vector);
|
|
668
|
+
eigenvalues.push(0.2); // Approximate
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
return {eigenvalues: eigenvalues.slice(0, k), eigenvectors: eigenvectors.slice(0, k)};
|
|
672
|
+
}
|
package/src/index.ts
CHANGED