ruvector 0.1.63 → 0.1.65
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/bin/cli.js +833 -24
- package/bin/mcp-server.js +357 -0
- package/dist/core/ast-parser.d.ts +108 -0
- package/dist/core/ast-parser.d.ts.map +1 -0
- package/dist/core/ast-parser.js +602 -0
- package/dist/core/cluster-wrapper.d.ts +148 -0
- package/dist/core/cluster-wrapper.d.ts.map +1 -0
- package/dist/core/cluster-wrapper.js +271 -0
- package/dist/core/coverage-router.d.ts +88 -0
- package/dist/core/coverage-router.d.ts.map +1 -0
- package/dist/core/coverage-router.js +315 -0
- package/dist/core/diff-embeddings.d.ts +93 -0
- package/dist/core/diff-embeddings.d.ts.map +1 -0
- package/dist/core/diff-embeddings.js +334 -0
- package/dist/core/graph-algorithms.d.ts +83 -0
- package/dist/core/graph-algorithms.d.ts.map +1 -0
- package/dist/core/graph-algorithms.js +514 -0
- package/dist/core/graph-wrapper.d.ts +147 -0
- package/dist/core/graph-wrapper.d.ts.map +1 -0
- package/dist/core/graph-wrapper.js +299 -0
- package/dist/core/index.d.ts +12 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +19 -1
- package/dist/core/router-wrapper.d.ts +62 -0
- package/dist/core/router-wrapper.d.ts.map +1 -0
- package/dist/core/router-wrapper.js +209 -0
- package/package.json +1 -1
- package/ruvector.db +0 -0
|
@@ -0,0 +1,514 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Graph Algorithms - MinCut, Spectral Clustering, Community Detection
|
|
4
|
+
*
|
|
5
|
+
* Provides graph partitioning and clustering algorithms for:
|
|
6
|
+
* - Code module detection
|
|
7
|
+
* - Dependency clustering
|
|
8
|
+
* - Architecture analysis
|
|
9
|
+
* - Refactoring suggestions
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.buildGraph = buildGraph;
|
|
13
|
+
exports.minCut = minCut;
|
|
14
|
+
exports.spectralClustering = spectralClustering;
|
|
15
|
+
exports.louvainCommunities = louvainCommunities;
|
|
16
|
+
exports.calculateModularity = calculateModularity;
|
|
17
|
+
exports.findBridges = findBridges;
|
|
18
|
+
exports.findArticulationPoints = findArticulationPoints;
|
|
19
|
+
/**
|
|
20
|
+
* Build adjacency representation from edges
|
|
21
|
+
*/
|
|
22
|
+
function buildGraph(nodes, edges) {
|
|
23
|
+
const adjacency = new Map();
|
|
24
|
+
for (const node of nodes) {
|
|
25
|
+
adjacency.set(node, new Map());
|
|
26
|
+
}
|
|
27
|
+
for (const { from, to, weight = 1 } of edges) {
|
|
28
|
+
if (!adjacency.has(from))
|
|
29
|
+
adjacency.set(from, new Map());
|
|
30
|
+
if (!adjacency.has(to))
|
|
31
|
+
adjacency.set(to, new Map());
|
|
32
|
+
// Undirected graph - add both directions
|
|
33
|
+
adjacency.get(from).set(to, weight);
|
|
34
|
+
adjacency.get(to).set(from, weight);
|
|
35
|
+
}
|
|
36
|
+
return { nodes, edges, adjacency };
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Minimum Cut (Stoer-Wagner algorithm)
|
|
40
|
+
*
|
|
41
|
+
* Finds the minimum weight cut that partitions the graph into two parts.
|
|
42
|
+
* Useful for finding loosely coupled module boundaries.
|
|
43
|
+
*/
|
|
44
|
+
function minCut(graph) {
|
|
45
|
+
const n = graph.nodes.length;
|
|
46
|
+
if (n < 2) {
|
|
47
|
+
return { groups: [graph.nodes], cutWeight: 0, modularity: 0 };
|
|
48
|
+
}
|
|
49
|
+
// Copy adjacency for modification
|
|
50
|
+
const adj = new Map();
|
|
51
|
+
for (const [node, neighbors] of graph.adjacency) {
|
|
52
|
+
adj.set(node, new Map(neighbors));
|
|
53
|
+
}
|
|
54
|
+
let minCutWeight = Infinity;
|
|
55
|
+
let bestPartition = [];
|
|
56
|
+
const merged = new Map(); // Track merged nodes
|
|
57
|
+
for (const node of graph.nodes) {
|
|
58
|
+
merged.set(node, [node]);
|
|
59
|
+
}
|
|
60
|
+
let remaining = [...graph.nodes];
|
|
61
|
+
// Stoer-Wagner phases
|
|
62
|
+
while (remaining.length > 1) {
|
|
63
|
+
// Maximum adjacency search
|
|
64
|
+
const inA = new Set([remaining[0]]);
|
|
65
|
+
const weights = new Map();
|
|
66
|
+
for (const node of remaining) {
|
|
67
|
+
if (!inA.has(node)) {
|
|
68
|
+
weights.set(node, adj.get(remaining[0])?.get(node) || 0);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
let lastAdded = remaining[0];
|
|
72
|
+
let beforeLast = remaining[0];
|
|
73
|
+
while (inA.size < remaining.length) {
|
|
74
|
+
// Find node with maximum weight to A
|
|
75
|
+
let maxWeight = -Infinity;
|
|
76
|
+
let maxNode = '';
|
|
77
|
+
for (const [node, weight] of weights) {
|
|
78
|
+
if (!inA.has(node) && weight > maxWeight) {
|
|
79
|
+
maxWeight = weight;
|
|
80
|
+
maxNode = node;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (!maxNode)
|
|
84
|
+
break;
|
|
85
|
+
beforeLast = lastAdded;
|
|
86
|
+
lastAdded = maxNode;
|
|
87
|
+
inA.add(maxNode);
|
|
88
|
+
// Update weights
|
|
89
|
+
for (const [neighbor, w] of adj.get(maxNode) || []) {
|
|
90
|
+
if (!inA.has(neighbor)) {
|
|
91
|
+
weights.set(neighbor, (weights.get(neighbor) || 0) + w);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// Cut of the phase
|
|
96
|
+
const cutWeight = weights.get(lastAdded) || 0;
|
|
97
|
+
if (cutWeight < minCutWeight) {
|
|
98
|
+
minCutWeight = cutWeight;
|
|
99
|
+
const lastGroup = merged.get(lastAdded) || [lastAdded];
|
|
100
|
+
const otherNodes = remaining.filter(n => n !== lastAdded).flatMap(n => merged.get(n) || [n]);
|
|
101
|
+
bestPartition = [lastGroup, otherNodes];
|
|
102
|
+
}
|
|
103
|
+
// Merge last two nodes
|
|
104
|
+
if (remaining.length > 1) {
|
|
105
|
+
// Merge lastAdded into beforeLast
|
|
106
|
+
const mergedNodes = [...(merged.get(beforeLast) || []), ...(merged.get(lastAdded) || [])];
|
|
107
|
+
merged.set(beforeLast, mergedNodes);
|
|
108
|
+
// Update adjacency
|
|
109
|
+
for (const [neighbor, w] of adj.get(lastAdded) || []) {
|
|
110
|
+
if (neighbor !== beforeLast) {
|
|
111
|
+
const current = adj.get(beforeLast)?.get(neighbor) || 0;
|
|
112
|
+
adj.get(beforeLast)?.set(neighbor, current + w);
|
|
113
|
+
adj.get(neighbor)?.set(beforeLast, current + w);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Remove lastAdded
|
|
117
|
+
remaining = remaining.filter(n => n !== lastAdded);
|
|
118
|
+
adj.delete(lastAdded);
|
|
119
|
+
for (const [, neighbors] of adj) {
|
|
120
|
+
neighbors.delete(lastAdded);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const modularity = calculateModularity(graph, bestPartition);
|
|
125
|
+
return {
|
|
126
|
+
groups: bestPartition.filter(g => g.length > 0),
|
|
127
|
+
cutWeight: minCutWeight,
|
|
128
|
+
modularity,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Spectral Clustering (using power iteration)
|
|
133
|
+
*
|
|
134
|
+
* Uses graph Laplacian eigenvectors for clustering.
|
|
135
|
+
* Good for finding natural clusters in code dependencies.
|
|
136
|
+
*/
|
|
137
|
+
function spectralClustering(graph, k = 2) {
|
|
138
|
+
const n = graph.nodes.length;
|
|
139
|
+
const nodeIndex = new Map(graph.nodes.map((node, i) => [node, i]));
|
|
140
|
+
const clusters = new Map();
|
|
141
|
+
if (n === 0) {
|
|
142
|
+
return { clusters, eigenvalues: [], coordinates: new Map() };
|
|
143
|
+
}
|
|
144
|
+
// Build Laplacian matrix (D - A)
|
|
145
|
+
const degree = new Float64Array(n);
|
|
146
|
+
const laplacian = Array(n).fill(null).map(() => Array(n).fill(0));
|
|
147
|
+
for (const [node, neighbors] of graph.adjacency) {
|
|
148
|
+
const i = nodeIndex.get(node);
|
|
149
|
+
let d = 0;
|
|
150
|
+
for (const [neighbor, weight] of neighbors) {
|
|
151
|
+
const j = nodeIndex.get(neighbor);
|
|
152
|
+
laplacian[i][j] = -weight;
|
|
153
|
+
d += weight;
|
|
154
|
+
}
|
|
155
|
+
degree[i] = d;
|
|
156
|
+
laplacian[i][i] = d;
|
|
157
|
+
}
|
|
158
|
+
// Normalized Laplacian: D^(-1/2) L D^(-1/2)
|
|
159
|
+
for (let i = 0; i < n; i++) {
|
|
160
|
+
for (let j = 0; j < n; j++) {
|
|
161
|
+
if (degree[i] > 0 && degree[j] > 0) {
|
|
162
|
+
laplacian[i][j] /= Math.sqrt(degree[i] * degree[j]);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
// Power iteration to find eigenvectors
|
|
167
|
+
const eigenvectors = [];
|
|
168
|
+
const eigenvalues = [];
|
|
169
|
+
for (let ev = 0; ev < Math.min(k, n); ev++) {
|
|
170
|
+
let vector = new Float64Array(n);
|
|
171
|
+
for (let i = 0; i < n; i++) {
|
|
172
|
+
vector[i] = Math.random();
|
|
173
|
+
}
|
|
174
|
+
normalize(vector);
|
|
175
|
+
// Deflation: orthogonalize against previous eigenvectors
|
|
176
|
+
for (const prev of eigenvectors) {
|
|
177
|
+
const dot = dotProduct(vector, new Float64Array(prev));
|
|
178
|
+
for (let i = 0; i < n; i++) {
|
|
179
|
+
vector[i] -= dot * prev[i];
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
normalize(vector);
|
|
183
|
+
// Power iteration
|
|
184
|
+
for (let iter = 0; iter < 100; iter++) {
|
|
185
|
+
const newVector = new Float64Array(n);
|
|
186
|
+
for (let i = 0; i < n; i++) {
|
|
187
|
+
for (let j = 0; j < n; j++) {
|
|
188
|
+
newVector[i] += laplacian[i][j] * vector[j];
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
// Deflation
|
|
192
|
+
for (const prev of eigenvectors) {
|
|
193
|
+
const dot = dotProduct(newVector, new Float64Array(prev));
|
|
194
|
+
for (let i = 0; i < n; i++) {
|
|
195
|
+
newVector[i] -= dot * prev[i];
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
normalize(newVector);
|
|
199
|
+
vector = newVector;
|
|
200
|
+
}
|
|
201
|
+
// Compute eigenvalue
|
|
202
|
+
let eigenvalue = 0;
|
|
203
|
+
for (let i = 0; i < n; i++) {
|
|
204
|
+
let sum = 0;
|
|
205
|
+
for (let j = 0; j < n; j++) {
|
|
206
|
+
sum += laplacian[i][j] * vector[j];
|
|
207
|
+
}
|
|
208
|
+
eigenvalue += vector[i] * sum;
|
|
209
|
+
}
|
|
210
|
+
eigenvectors.push(Array.from(vector));
|
|
211
|
+
eigenvalues.push(eigenvalue);
|
|
212
|
+
}
|
|
213
|
+
// K-means clustering on eigenvector coordinates
|
|
214
|
+
const coordinates = new Map();
|
|
215
|
+
for (let i = 0; i < n; i++) {
|
|
216
|
+
coordinates.set(graph.nodes[i], eigenvectors.map(ev => ev[i]));
|
|
217
|
+
}
|
|
218
|
+
// Simple k-means
|
|
219
|
+
const clusterAssignment = kMeans(graph.nodes.map(node => coordinates.get(node)), k);
|
|
220
|
+
for (let i = 0; i < n; i++) {
|
|
221
|
+
clusters.set(graph.nodes[i], clusterAssignment[i]);
|
|
222
|
+
}
|
|
223
|
+
return { clusters, eigenvalues, coordinates };
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Louvain Community Detection
|
|
227
|
+
*
|
|
228
|
+
* Greedy modularity optimization for finding communities.
|
|
229
|
+
* Good for detecting natural module boundaries.
|
|
230
|
+
*/
|
|
231
|
+
function louvainCommunities(graph) {
|
|
232
|
+
const communities = new Map();
|
|
233
|
+
let communityId = 0;
|
|
234
|
+
// Initialize: each node in its own community
|
|
235
|
+
for (const node of graph.nodes) {
|
|
236
|
+
communities.set(node, communityId++);
|
|
237
|
+
}
|
|
238
|
+
// Total edge weight
|
|
239
|
+
let m = 0;
|
|
240
|
+
for (const { weight = 1 } of graph.edges) {
|
|
241
|
+
m += weight;
|
|
242
|
+
}
|
|
243
|
+
m /= 2; // Undirected
|
|
244
|
+
if (m === 0)
|
|
245
|
+
return communities;
|
|
246
|
+
// Node weights (sum of edge weights)
|
|
247
|
+
const nodeWeight = new Map();
|
|
248
|
+
for (const node of graph.nodes) {
|
|
249
|
+
let w = 0;
|
|
250
|
+
for (const [, weight] of graph.adjacency.get(node) || []) {
|
|
251
|
+
w += weight;
|
|
252
|
+
}
|
|
253
|
+
nodeWeight.set(node, w);
|
|
254
|
+
}
|
|
255
|
+
// Community weights
|
|
256
|
+
const communityWeight = new Map();
|
|
257
|
+
for (const node of graph.nodes) {
|
|
258
|
+
const c = communities.get(node);
|
|
259
|
+
communityWeight.set(c, (communityWeight.get(c) || 0) + (nodeWeight.get(node) || 0));
|
|
260
|
+
}
|
|
261
|
+
// Iterate until no improvement
|
|
262
|
+
let improved = true;
|
|
263
|
+
while (improved) {
|
|
264
|
+
improved = false;
|
|
265
|
+
for (const node of graph.nodes) {
|
|
266
|
+
const currentCommunity = communities.get(node);
|
|
267
|
+
const ki = nodeWeight.get(node) || 0;
|
|
268
|
+
// Calculate modularity gain for moving to neighbor communities
|
|
269
|
+
let bestCommunity = currentCommunity;
|
|
270
|
+
let bestGain = 0;
|
|
271
|
+
const neighborCommunities = new Set();
|
|
272
|
+
for (const [neighbor] of graph.adjacency.get(node) || []) {
|
|
273
|
+
neighborCommunities.add(communities.get(neighbor));
|
|
274
|
+
}
|
|
275
|
+
for (const targetCommunity of neighborCommunities) {
|
|
276
|
+
if (targetCommunity === currentCommunity)
|
|
277
|
+
continue;
|
|
278
|
+
// Calculate edge weight to target community
|
|
279
|
+
let ki_in = 0;
|
|
280
|
+
for (const [neighbor, weight] of graph.adjacency.get(node) || []) {
|
|
281
|
+
if (communities.get(neighbor) === targetCommunity) {
|
|
282
|
+
ki_in += weight;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
const sumTot = communityWeight.get(targetCommunity) || 0;
|
|
286
|
+
const gain = ki_in / m - (ki * sumTot) / (2 * m * m);
|
|
287
|
+
if (gain > bestGain) {
|
|
288
|
+
bestGain = gain;
|
|
289
|
+
bestCommunity = targetCommunity;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
// Move node if beneficial
|
|
293
|
+
if (bestCommunity !== currentCommunity) {
|
|
294
|
+
communities.set(node, bestCommunity);
|
|
295
|
+
// Update community weights
|
|
296
|
+
communityWeight.set(currentCommunity, (communityWeight.get(currentCommunity) || 0) - ki);
|
|
297
|
+
communityWeight.set(bestCommunity, (communityWeight.get(bestCommunity) || 0) + ki);
|
|
298
|
+
improved = true;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
// Renumber communities to be contiguous
|
|
303
|
+
const renumber = new Map();
|
|
304
|
+
let newId = 0;
|
|
305
|
+
for (const [node, c] of communities) {
|
|
306
|
+
if (!renumber.has(c)) {
|
|
307
|
+
renumber.set(c, newId++);
|
|
308
|
+
}
|
|
309
|
+
communities.set(node, renumber.get(c));
|
|
310
|
+
}
|
|
311
|
+
return communities;
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Calculate modularity of a partition
|
|
315
|
+
*/
|
|
316
|
+
function calculateModularity(graph, partition) {
|
|
317
|
+
let m = 0;
|
|
318
|
+
for (const { weight = 1 } of graph.edges) {
|
|
319
|
+
m += weight;
|
|
320
|
+
}
|
|
321
|
+
m /= 2;
|
|
322
|
+
if (m === 0)
|
|
323
|
+
return 0;
|
|
324
|
+
let modularity = 0;
|
|
325
|
+
for (const group of partition) {
|
|
326
|
+
const groupSet = new Set(group);
|
|
327
|
+
// Edges within group
|
|
328
|
+
let inGroup = 0;
|
|
329
|
+
let degreeSum = 0;
|
|
330
|
+
for (const node of group) {
|
|
331
|
+
for (const [neighbor, weight] of graph.adjacency.get(node) || []) {
|
|
332
|
+
if (groupSet.has(neighbor)) {
|
|
333
|
+
inGroup += weight;
|
|
334
|
+
}
|
|
335
|
+
degreeSum += weight;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
inGroup /= 2; // Count each edge once
|
|
339
|
+
modularity += inGroup / m - Math.pow(degreeSum / (2 * m), 2);
|
|
340
|
+
}
|
|
341
|
+
return modularity;
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Find bridges (edges whose removal disconnects components)
|
|
345
|
+
*/
|
|
346
|
+
function findBridges(graph) {
|
|
347
|
+
const bridges = [];
|
|
348
|
+
const visited = new Set();
|
|
349
|
+
const discovery = new Map();
|
|
350
|
+
const low = new Map();
|
|
351
|
+
const parent = new Map();
|
|
352
|
+
let time = 0;
|
|
353
|
+
function dfs(node) {
|
|
354
|
+
visited.add(node);
|
|
355
|
+
discovery.set(node, time);
|
|
356
|
+
low.set(node, time);
|
|
357
|
+
time++;
|
|
358
|
+
for (const [neighbor] of graph.adjacency.get(node) || []) {
|
|
359
|
+
if (!visited.has(neighbor)) {
|
|
360
|
+
parent.set(neighbor, node);
|
|
361
|
+
dfs(neighbor);
|
|
362
|
+
low.set(node, Math.min(low.get(node), low.get(neighbor)));
|
|
363
|
+
if (low.get(neighbor) > discovery.get(node)) {
|
|
364
|
+
bridges.push({ from: node, to: neighbor });
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
else if (neighbor !== parent.get(node)) {
|
|
368
|
+
low.set(node, Math.min(low.get(node), discovery.get(neighbor)));
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
for (const node of graph.nodes) {
|
|
373
|
+
if (!visited.has(node)) {
|
|
374
|
+
parent.set(node, null);
|
|
375
|
+
dfs(node);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
return bridges;
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Find articulation points (nodes whose removal disconnects components)
|
|
382
|
+
*/
|
|
383
|
+
function findArticulationPoints(graph) {
|
|
384
|
+
const points = [];
|
|
385
|
+
const visited = new Set();
|
|
386
|
+
const discovery = new Map();
|
|
387
|
+
const low = new Map();
|
|
388
|
+
const parent = new Map();
|
|
389
|
+
let time = 0;
|
|
390
|
+
function dfs(node) {
|
|
391
|
+
visited.add(node);
|
|
392
|
+
discovery.set(node, time);
|
|
393
|
+
low.set(node, time);
|
|
394
|
+
time++;
|
|
395
|
+
let children = 0;
|
|
396
|
+
for (const [neighbor] of graph.adjacency.get(node) || []) {
|
|
397
|
+
if (!visited.has(neighbor)) {
|
|
398
|
+
children++;
|
|
399
|
+
parent.set(neighbor, node);
|
|
400
|
+
dfs(neighbor);
|
|
401
|
+
low.set(node, Math.min(low.get(node), low.get(neighbor)));
|
|
402
|
+
// Root with 2+ children or non-root with low[v] >= disc[u]
|
|
403
|
+
if ((parent.get(node) === null && children > 1) ||
|
|
404
|
+
(parent.get(node) !== null && low.get(neighbor) >= discovery.get(node))) {
|
|
405
|
+
if (!points.includes(node)) {
|
|
406
|
+
points.push(node);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
else if (neighbor !== parent.get(node)) {
|
|
411
|
+
low.set(node, Math.min(low.get(node), discovery.get(neighbor)));
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
for (const node of graph.nodes) {
|
|
416
|
+
if (!visited.has(node)) {
|
|
417
|
+
parent.set(node, null);
|
|
418
|
+
dfs(node);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
return points;
|
|
422
|
+
}
|
|
423
|
+
// Helper functions
|
|
424
|
+
function normalize(v) {
|
|
425
|
+
let sum = 0;
|
|
426
|
+
for (let i = 0; i < v.length; i++) {
|
|
427
|
+
sum += v[i] * v[i];
|
|
428
|
+
}
|
|
429
|
+
const norm = Math.sqrt(sum);
|
|
430
|
+
if (norm > 0) {
|
|
431
|
+
for (let i = 0; i < v.length; i++) {
|
|
432
|
+
v[i] /= norm;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
function dotProduct(a, b) {
|
|
437
|
+
let sum = 0;
|
|
438
|
+
for (let i = 0; i < a.length; i++) {
|
|
439
|
+
sum += a[i] * b[i];
|
|
440
|
+
}
|
|
441
|
+
return sum;
|
|
442
|
+
}
|
|
443
|
+
function kMeans(points, k, maxIter = 100) {
|
|
444
|
+
const n = points.length;
|
|
445
|
+
if (n === 0 || k === 0)
|
|
446
|
+
return [];
|
|
447
|
+
const dim = points[0].length;
|
|
448
|
+
// Random initialization
|
|
449
|
+
const centroids = [];
|
|
450
|
+
const used = new Set();
|
|
451
|
+
while (centroids.length < Math.min(k, n)) {
|
|
452
|
+
const idx = Math.floor(Math.random() * n);
|
|
453
|
+
if (!used.has(idx)) {
|
|
454
|
+
used.add(idx);
|
|
455
|
+
centroids.push([...points[idx]]);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
const assignment = new Array(n).fill(0);
|
|
459
|
+
for (let iter = 0; iter < maxIter; iter++) {
|
|
460
|
+
// Assign points to nearest centroid
|
|
461
|
+
let changed = false;
|
|
462
|
+
for (let i = 0; i < n; i++) {
|
|
463
|
+
let minDist = Infinity;
|
|
464
|
+
let minC = 0;
|
|
465
|
+
for (let c = 0; c < centroids.length; c++) {
|
|
466
|
+
let dist = 0;
|
|
467
|
+
for (let d = 0; d < dim; d++) {
|
|
468
|
+
dist += Math.pow(points[i][d] - centroids[c][d], 2);
|
|
469
|
+
}
|
|
470
|
+
if (dist < minDist) {
|
|
471
|
+
minDist = dist;
|
|
472
|
+
minC = c;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
if (assignment[i] !== minC) {
|
|
476
|
+
assignment[i] = minC;
|
|
477
|
+
changed = true;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
if (!changed)
|
|
481
|
+
break;
|
|
482
|
+
// Update centroids
|
|
483
|
+
const counts = new Array(k).fill(0);
|
|
484
|
+
for (let c = 0; c < centroids.length; c++) {
|
|
485
|
+
for (let d = 0; d < dim; d++) {
|
|
486
|
+
centroids[c][d] = 0;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
for (let i = 0; i < n; i++) {
|
|
490
|
+
const c = assignment[i];
|
|
491
|
+
counts[c]++;
|
|
492
|
+
for (let d = 0; d < dim; d++) {
|
|
493
|
+
centroids[c][d] += points[i][d];
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
for (let c = 0; c < centroids.length; c++) {
|
|
497
|
+
if (counts[c] > 0) {
|
|
498
|
+
for (let d = 0; d < dim; d++) {
|
|
499
|
+
centroids[c][d] /= counts[c];
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
return assignment;
|
|
505
|
+
}
|
|
506
|
+
exports.default = {
|
|
507
|
+
buildGraph,
|
|
508
|
+
minCut,
|
|
509
|
+
spectralClustering,
|
|
510
|
+
louvainCommunities,
|
|
511
|
+
calculateModularity,
|
|
512
|
+
findBridges,
|
|
513
|
+
findArticulationPoints,
|
|
514
|
+
};
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Graph Wrapper - Hypergraph database for code relationships
|
|
3
|
+
*
|
|
4
|
+
* Wraps @ruvector/graph-node for dependency analysis, co-edit patterns,
|
|
5
|
+
* and code structure understanding.
|
|
6
|
+
*/
|
|
7
|
+
export declare function isGraphAvailable(): boolean;
|
|
8
|
+
export interface Node {
|
|
9
|
+
id: string;
|
|
10
|
+
labels: string[];
|
|
11
|
+
properties: Record<string, any>;
|
|
12
|
+
}
|
|
13
|
+
export interface Edge {
|
|
14
|
+
id?: string;
|
|
15
|
+
from: string;
|
|
16
|
+
to: string;
|
|
17
|
+
type: string;
|
|
18
|
+
properties?: Record<string, any>;
|
|
19
|
+
}
|
|
20
|
+
export interface Hyperedge {
|
|
21
|
+
id?: string;
|
|
22
|
+
nodes: string[];
|
|
23
|
+
type: string;
|
|
24
|
+
properties?: Record<string, any>;
|
|
25
|
+
}
|
|
26
|
+
export interface CypherResult {
|
|
27
|
+
columns: string[];
|
|
28
|
+
rows: any[][];
|
|
29
|
+
}
|
|
30
|
+
export interface PathResult {
|
|
31
|
+
nodes: Node[];
|
|
32
|
+
edges: Edge[];
|
|
33
|
+
length: number;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Graph Database for code relationships
|
|
37
|
+
*/
|
|
38
|
+
export declare class CodeGraph {
|
|
39
|
+
private inner;
|
|
40
|
+
private storagePath?;
|
|
41
|
+
constructor(options?: {
|
|
42
|
+
storagePath?: string;
|
|
43
|
+
inMemory?: boolean;
|
|
44
|
+
});
|
|
45
|
+
/**
|
|
46
|
+
* Create a node (file, function, class, etc.)
|
|
47
|
+
*/
|
|
48
|
+
createNode(id: string, labels: string[], properties?: Record<string, any>): Node;
|
|
49
|
+
/**
|
|
50
|
+
* Get a node by ID
|
|
51
|
+
*/
|
|
52
|
+
getNode(id: string): Node | null;
|
|
53
|
+
/**
|
|
54
|
+
* Update node properties
|
|
55
|
+
*/
|
|
56
|
+
updateNode(id: string, properties: Record<string, any>): boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Delete a node
|
|
59
|
+
*/
|
|
60
|
+
deleteNode(id: string): boolean;
|
|
61
|
+
/**
|
|
62
|
+
* Find nodes by label
|
|
63
|
+
*/
|
|
64
|
+
findNodesByLabel(label: string): Node[];
|
|
65
|
+
/**
|
|
66
|
+
* Create an edge (import, call, reference, etc.)
|
|
67
|
+
*/
|
|
68
|
+
createEdge(from: string, to: string, type: string, properties?: Record<string, any>): Edge;
|
|
69
|
+
/**
|
|
70
|
+
* Get edges from a node
|
|
71
|
+
*/
|
|
72
|
+
getOutgoingEdges(nodeId: string, type?: string): Edge[];
|
|
73
|
+
/**
|
|
74
|
+
* Get edges to a node
|
|
75
|
+
*/
|
|
76
|
+
getIncomingEdges(nodeId: string, type?: string): Edge[];
|
|
77
|
+
/**
|
|
78
|
+
* Delete an edge
|
|
79
|
+
*/
|
|
80
|
+
deleteEdge(edgeId: string): boolean;
|
|
81
|
+
/**
|
|
82
|
+
* Create a hyperedge connecting multiple nodes
|
|
83
|
+
*/
|
|
84
|
+
createHyperedge(nodes: string[], type: string, properties?: Record<string, any>): Hyperedge;
|
|
85
|
+
/**
|
|
86
|
+
* Get hyperedges containing a node
|
|
87
|
+
*/
|
|
88
|
+
getHyperedges(nodeId: string, type?: string): Hyperedge[];
|
|
89
|
+
/**
|
|
90
|
+
* Execute a Cypher query
|
|
91
|
+
*/
|
|
92
|
+
cypher(query: string, params?: Record<string, any>): CypherResult;
|
|
93
|
+
/**
|
|
94
|
+
* Find shortest path between nodes
|
|
95
|
+
*/
|
|
96
|
+
shortestPath(from: string, to: string, maxDepth?: number): PathResult | null;
|
|
97
|
+
/**
|
|
98
|
+
* Get all paths between nodes (up to maxPaths)
|
|
99
|
+
*/
|
|
100
|
+
allPaths(from: string, to: string, maxDepth?: number, maxPaths?: number): PathResult[];
|
|
101
|
+
/**
|
|
102
|
+
* Get neighbors of a node
|
|
103
|
+
*/
|
|
104
|
+
neighbors(nodeId: string, depth?: number): Node[];
|
|
105
|
+
/**
|
|
106
|
+
* Calculate PageRank for nodes
|
|
107
|
+
*/
|
|
108
|
+
pageRank(iterations?: number, dampingFactor?: number): Map<string, number>;
|
|
109
|
+
/**
|
|
110
|
+
* Find connected components
|
|
111
|
+
*/
|
|
112
|
+
connectedComponents(): string[][];
|
|
113
|
+
/**
|
|
114
|
+
* Detect communities (Louvain algorithm)
|
|
115
|
+
*/
|
|
116
|
+
communities(): Map<string, number>;
|
|
117
|
+
/**
|
|
118
|
+
* Calculate betweenness centrality
|
|
119
|
+
*/
|
|
120
|
+
betweennessCentrality(): Map<string, number>;
|
|
121
|
+
/**
|
|
122
|
+
* Save graph to storage
|
|
123
|
+
*/
|
|
124
|
+
save(): void;
|
|
125
|
+
/**
|
|
126
|
+
* Load graph from storage
|
|
127
|
+
*/
|
|
128
|
+
load(): void;
|
|
129
|
+
/**
|
|
130
|
+
* Clear all data
|
|
131
|
+
*/
|
|
132
|
+
clear(): void;
|
|
133
|
+
/**
|
|
134
|
+
* Get graph statistics
|
|
135
|
+
*/
|
|
136
|
+
stats(): {
|
|
137
|
+
nodes: number;
|
|
138
|
+
edges: number;
|
|
139
|
+
hyperedges: number;
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Create a code dependency graph from file analysis
|
|
144
|
+
*/
|
|
145
|
+
export declare function createCodeDependencyGraph(storagePath?: string): CodeGraph;
|
|
146
|
+
export default CodeGraph;
|
|
147
|
+
//# sourceMappingURL=graph-wrapper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graph-wrapper.d.ts","sourceRoot":"","sources":["../../src/core/graph-wrapper.ts"],"names":[],"mappings":"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"}
|