@sparkleideas/plugins 3.0.0-alpha.10
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 +401 -0
- package/__tests__/collection-manager.test.ts +332 -0
- package/__tests__/dependency-graph.test.ts +434 -0
- package/__tests__/enhanced-plugin-registry.test.ts +488 -0
- package/__tests__/plugin-registry.test.ts +368 -0
- package/__tests__/ruvector-bridge.test.ts +2429 -0
- package/__tests__/ruvector-integration.test.ts +1602 -0
- package/__tests__/ruvector-migrations.test.ts +1099 -0
- package/__tests__/ruvector-quantization.test.ts +846 -0
- package/__tests__/ruvector-streaming.test.ts +1088 -0
- package/__tests__/sdk.test.ts +325 -0
- package/__tests__/security.test.ts +348 -0
- package/__tests__/utils/ruvector-test-utils.ts +860 -0
- package/examples/plugin-creator/index.ts +636 -0
- package/examples/plugin-creator/plugin-creator.test.ts +312 -0
- package/examples/ruvector/README.md +288 -0
- package/examples/ruvector/attention-patterns.ts +394 -0
- package/examples/ruvector/basic-usage.ts +288 -0
- package/examples/ruvector/docker-compose.yml +75 -0
- package/examples/ruvector/gnn-analysis.ts +501 -0
- package/examples/ruvector/hyperbolic-hierarchies.ts +557 -0
- package/examples/ruvector/init-db.sql +119 -0
- package/examples/ruvector/quantization.ts +680 -0
- package/examples/ruvector/self-learning.ts +447 -0
- package/examples/ruvector/semantic-search.ts +576 -0
- package/examples/ruvector/streaming-large-data.ts +507 -0
- package/examples/ruvector/transactions.ts +594 -0
- package/examples/ruvector-plugins/hook-pattern-library.ts +486 -0
- package/examples/ruvector-plugins/index.ts +79 -0
- package/examples/ruvector-plugins/intent-router.ts +354 -0
- package/examples/ruvector-plugins/mcp-tool-optimizer.ts +424 -0
- package/examples/ruvector-plugins/reasoning-bank.ts +657 -0
- package/examples/ruvector-plugins/ruvector-plugins.test.ts +518 -0
- package/examples/ruvector-plugins/semantic-code-search.ts +498 -0
- package/examples/ruvector-plugins/shared/index.ts +20 -0
- package/examples/ruvector-plugins/shared/vector-utils.ts +257 -0
- package/examples/ruvector-plugins/sona-learning.ts +445 -0
- package/package.json +97 -0
- package/src/collections/collection-manager.ts +661 -0
- package/src/collections/index.ts +56 -0
- package/src/collections/official/index.ts +1040 -0
- package/src/core/base-plugin.ts +416 -0
- package/src/core/plugin-interface.ts +215 -0
- package/src/hooks/index.ts +685 -0
- package/src/index.ts +378 -0
- package/src/integrations/agentic-flow.ts +743 -0
- package/src/integrations/index.ts +88 -0
- package/src/integrations/ruvector/ARCHITECTURE.md +1245 -0
- package/src/integrations/ruvector/attention-advanced.ts +1040 -0
- package/src/integrations/ruvector/attention-executor.ts +782 -0
- package/src/integrations/ruvector/attention-mechanisms.ts +757 -0
- package/src/integrations/ruvector/attention.ts +1063 -0
- package/src/integrations/ruvector/gnn.ts +3050 -0
- package/src/integrations/ruvector/hyperbolic.ts +1948 -0
- package/src/integrations/ruvector/index.ts +394 -0
- package/src/integrations/ruvector/migrations/001_create_extension.sql +135 -0
- package/src/integrations/ruvector/migrations/002_create_vector_tables.sql +259 -0
- package/src/integrations/ruvector/migrations/003_create_indices.sql +328 -0
- package/src/integrations/ruvector/migrations/004_create_functions.sql +598 -0
- package/src/integrations/ruvector/migrations/005_create_attention_functions.sql +654 -0
- package/src/integrations/ruvector/migrations/006_create_gnn_functions.sql +728 -0
- package/src/integrations/ruvector/migrations/007_create_hyperbolic_functions.sql +762 -0
- package/src/integrations/ruvector/migrations/index.ts +35 -0
- package/src/integrations/ruvector/migrations/migrations.ts +647 -0
- package/src/integrations/ruvector/quantization.ts +2036 -0
- package/src/integrations/ruvector/ruvector-bridge.ts +2000 -0
- package/src/integrations/ruvector/self-learning.ts +2376 -0
- package/src/integrations/ruvector/streaming.ts +1737 -0
- package/src/integrations/ruvector/types.ts +1945 -0
- package/src/providers/index.ts +643 -0
- package/src/registry/dependency-graph.ts +568 -0
- package/src/registry/enhanced-plugin-registry.ts +994 -0
- package/src/registry/plugin-registry.ts +604 -0
- package/src/sdk/index.ts +563 -0
- package/src/security/index.ts +594 -0
- package/src/types/index.ts +446 -0
- package/src/workers/index.ts +700 -0
- package/tmp.json +0 -0
- package/tsconfig.json +25 -0
- package/vitest.config.ts +23 -0
|
@@ -0,0 +1,728 @@
|
|
|
1
|
+
-- ============================================================================
|
|
2
|
+
-- Migration 006: Create Graph Neural Network Functions
|
|
3
|
+
-- RuVector PostgreSQL Bridge - Claude Flow V3
|
|
4
|
+
--
|
|
5
|
+
-- Creates SQL functions for GNN operations including GCN, GAT, and GraphSAGE
|
|
6
|
+
-- layer implementations.
|
|
7
|
+
-- Compatible with PostgreSQL 14+ and pgvector 0.5+
|
|
8
|
+
-- ============================================================================
|
|
9
|
+
|
|
10
|
+
BEGIN;
|
|
11
|
+
|
|
12
|
+
-- ----------------------------------------------------------------------------
|
|
13
|
+
-- Helper: ReLU Activation
|
|
14
|
+
-- ----------------------------------------------------------------------------
|
|
15
|
+
|
|
16
|
+
CREATE OR REPLACE FUNCTION claude_flow.relu(
|
|
17
|
+
x REAL[]
|
|
18
|
+
) RETURNS REAL[] AS $$
|
|
19
|
+
DECLARE
|
|
20
|
+
result REAL[];
|
|
21
|
+
i INTEGER;
|
|
22
|
+
BEGIN
|
|
23
|
+
result := ARRAY[]::REAL[];
|
|
24
|
+
FOR i IN 1..array_length(x, 1) LOOP
|
|
25
|
+
result := array_append(result, GREATEST(0, x[i]));
|
|
26
|
+
END LOOP;
|
|
27
|
+
RETURN result;
|
|
28
|
+
END;
|
|
29
|
+
$$ LANGUAGE plpgsql IMMUTABLE STRICT PARALLEL SAFE;
|
|
30
|
+
|
|
31
|
+
-- ----------------------------------------------------------------------------
|
|
32
|
+
-- Helper: LeakyReLU Activation
|
|
33
|
+
-- ----------------------------------------------------------------------------
|
|
34
|
+
|
|
35
|
+
CREATE OR REPLACE FUNCTION claude_flow.leaky_relu(
|
|
36
|
+
x REAL[],
|
|
37
|
+
negative_slope REAL DEFAULT 0.2
|
|
38
|
+
) RETURNS REAL[] AS $$
|
|
39
|
+
DECLARE
|
|
40
|
+
result REAL[];
|
|
41
|
+
i INTEGER;
|
|
42
|
+
BEGIN
|
|
43
|
+
result := ARRAY[]::REAL[];
|
|
44
|
+
FOR i IN 1..array_length(x, 1) LOOP
|
|
45
|
+
IF x[i] >= 0 THEN
|
|
46
|
+
result := array_append(result, x[i]);
|
|
47
|
+
ELSE
|
|
48
|
+
result := array_append(result, negative_slope * x[i]);
|
|
49
|
+
END IF;
|
|
50
|
+
END LOOP;
|
|
51
|
+
RETURN result;
|
|
52
|
+
END;
|
|
53
|
+
$$ LANGUAGE plpgsql IMMUTABLE STRICT PARALLEL SAFE;
|
|
54
|
+
|
|
55
|
+
-- ----------------------------------------------------------------------------
|
|
56
|
+
-- Helper: Matrix-Vector Multiplication
|
|
57
|
+
-- ----------------------------------------------------------------------------
|
|
58
|
+
|
|
59
|
+
CREATE OR REPLACE FUNCTION claude_flow.matmul(
|
|
60
|
+
matrix REAL[][],
|
|
61
|
+
vec REAL[]
|
|
62
|
+
) RETURNS REAL[] AS $$
|
|
63
|
+
DECLARE
|
|
64
|
+
result REAL[];
|
|
65
|
+
rows INTEGER;
|
|
66
|
+
cols INTEGER;
|
|
67
|
+
i INTEGER;
|
|
68
|
+
j INTEGER;
|
|
69
|
+
sum_val REAL;
|
|
70
|
+
BEGIN
|
|
71
|
+
rows := array_length(matrix, 1);
|
|
72
|
+
cols := array_length(matrix[1], 1);
|
|
73
|
+
|
|
74
|
+
IF cols != array_length(vec, 1) THEN
|
|
75
|
+
RAISE EXCEPTION 'Dimension mismatch: matrix cols (%) != vector length (%)', cols, array_length(vec, 1);
|
|
76
|
+
END IF;
|
|
77
|
+
|
|
78
|
+
result := ARRAY[]::REAL[];
|
|
79
|
+
FOR i IN 1..rows LOOP
|
|
80
|
+
sum_val := 0;
|
|
81
|
+
FOR j IN 1..cols LOOP
|
|
82
|
+
sum_val := sum_val + matrix[i][j] * vec[j];
|
|
83
|
+
END LOOP;
|
|
84
|
+
result := array_append(result, sum_val);
|
|
85
|
+
END LOOP;
|
|
86
|
+
|
|
87
|
+
RETURN result;
|
|
88
|
+
END;
|
|
89
|
+
$$ LANGUAGE plpgsql IMMUTABLE STRICT PARALLEL SAFE;
|
|
90
|
+
|
|
91
|
+
-- ----------------------------------------------------------------------------
|
|
92
|
+
-- GCN Layer (Graph Convolutional Network)
|
|
93
|
+
-- H' = σ(D^(-1/2) * A * D^(-1/2) * H * W)
|
|
94
|
+
-- Simplified: H' = σ(Σ_j (1/sqrt(d_i * d_j)) * h_j * W)
|
|
95
|
+
-- ----------------------------------------------------------------------------
|
|
96
|
+
|
|
97
|
+
CREATE OR REPLACE FUNCTION claude_flow.gcn_layer(
|
|
98
|
+
nodes REAL[][], -- Node feature matrix [num_nodes, in_features]
|
|
99
|
+
edges INTEGER[][], -- Edge list [[src, dst], ...] (0-indexed)
|
|
100
|
+
weights REAL[][] -- Weight matrix [in_features, out_features]
|
|
101
|
+
) RETURNS TABLE (
|
|
102
|
+
output_features REAL[][],
|
|
103
|
+
node_degrees INTEGER[]
|
|
104
|
+
) AS $$
|
|
105
|
+
DECLARE
|
|
106
|
+
num_nodes INTEGER;
|
|
107
|
+
in_features INTEGER;
|
|
108
|
+
out_features INTEGER;
|
|
109
|
+
degrees INTEGER[];
|
|
110
|
+
aggregated REAL[][];
|
|
111
|
+
transformed REAL[][];
|
|
112
|
+
i INTEGER;
|
|
113
|
+
j INTEGER;
|
|
114
|
+
src INTEGER;
|
|
115
|
+
dst INTEGER;
|
|
116
|
+
neighbor_sum REAL[];
|
|
117
|
+
d_i REAL;
|
|
118
|
+
d_j REAL;
|
|
119
|
+
norm_factor REAL;
|
|
120
|
+
BEGIN
|
|
121
|
+
-- Get dimensions
|
|
122
|
+
num_nodes := array_length(nodes, 1);
|
|
123
|
+
in_features := array_length(nodes[1], 1);
|
|
124
|
+
out_features := array_length(weights[1], 1);
|
|
125
|
+
|
|
126
|
+
-- Calculate node degrees
|
|
127
|
+
degrees := ARRAY[]::INTEGER[];
|
|
128
|
+
FOR i IN 1..num_nodes LOOP
|
|
129
|
+
degrees := array_append(degrees, 1); -- Self-loop
|
|
130
|
+
END LOOP;
|
|
131
|
+
|
|
132
|
+
FOR i IN 1..array_length(edges, 1) LOOP
|
|
133
|
+
src := edges[i][1] + 1; -- Convert 0-indexed to 1-indexed
|
|
134
|
+
dst := edges[i][2] + 1;
|
|
135
|
+
IF src >= 1 AND src <= num_nodes THEN
|
|
136
|
+
degrees[src] := degrees[src] + 1;
|
|
137
|
+
END IF;
|
|
138
|
+
IF dst >= 1 AND dst <= num_nodes AND src != dst THEN
|
|
139
|
+
degrees[dst] := degrees[dst] + 1;
|
|
140
|
+
END IF;
|
|
141
|
+
END LOOP;
|
|
142
|
+
|
|
143
|
+
-- Aggregate neighbor features with symmetric normalization
|
|
144
|
+
aggregated := ARRAY[]::REAL[][];
|
|
145
|
+
FOR i IN 1..num_nodes LOOP
|
|
146
|
+
-- Initialize with self-feature (self-loop)
|
|
147
|
+
neighbor_sum := ARRAY[]::REAL[];
|
|
148
|
+
d_i := sqrt(degrees[i]::REAL);
|
|
149
|
+
|
|
150
|
+
FOR j IN 1..in_features LOOP
|
|
151
|
+
neighbor_sum := array_append(neighbor_sum, nodes[i][j] / d_i); -- Self contribution
|
|
152
|
+
END LOOP;
|
|
153
|
+
|
|
154
|
+
aggregated := array_cat(aggregated, ARRAY[neighbor_sum]);
|
|
155
|
+
END LOOP;
|
|
156
|
+
|
|
157
|
+
-- Add neighbor contributions
|
|
158
|
+
FOR i IN 1..array_length(edges, 1) LOOP
|
|
159
|
+
src := edges[i][1] + 1;
|
|
160
|
+
dst := edges[i][2] + 1;
|
|
161
|
+
|
|
162
|
+
IF src >= 1 AND src <= num_nodes AND dst >= 1 AND dst <= num_nodes THEN
|
|
163
|
+
d_i := sqrt(degrees[dst]::REAL);
|
|
164
|
+
d_j := sqrt(degrees[src]::REAL);
|
|
165
|
+
norm_factor := 1.0 / (d_i * d_j);
|
|
166
|
+
|
|
167
|
+
FOR j IN 1..in_features LOOP
|
|
168
|
+
aggregated[dst][j] := aggregated[dst][j] + norm_factor * nodes[src][j];
|
|
169
|
+
END LOOP;
|
|
170
|
+
END IF;
|
|
171
|
+
END LOOP;
|
|
172
|
+
|
|
173
|
+
-- Apply weights and activation
|
|
174
|
+
transformed := ARRAY[]::REAL[][];
|
|
175
|
+
FOR i IN 1..num_nodes LOOP
|
|
176
|
+
DECLARE
|
|
177
|
+
node_output REAL[];
|
|
178
|
+
BEGIN
|
|
179
|
+
node_output := claude_flow.matmul(weights, aggregated[i]);
|
|
180
|
+
node_output := claude_flow.relu(node_output);
|
|
181
|
+
transformed := array_cat(transformed, ARRAY[node_output]);
|
|
182
|
+
END;
|
|
183
|
+
END LOOP;
|
|
184
|
+
|
|
185
|
+
output_features := transformed;
|
|
186
|
+
node_degrees := degrees;
|
|
187
|
+
RETURN NEXT;
|
|
188
|
+
END;
|
|
189
|
+
$$ LANGUAGE plpgsql STABLE;
|
|
190
|
+
|
|
191
|
+
-- ----------------------------------------------------------------------------
|
|
192
|
+
-- GAT Layer (Graph Attention Network)
|
|
193
|
+
-- Attention coefficients: α_ij = softmax_j(LeakyReLU(a^T [W*h_i || W*h_j]))
|
|
194
|
+
-- H' = σ(Σ_j α_ij * W * h_j)
|
|
195
|
+
-- ----------------------------------------------------------------------------
|
|
196
|
+
|
|
197
|
+
CREATE OR REPLACE FUNCTION claude_flow.gat_layer(
|
|
198
|
+
nodes REAL[][], -- Node feature matrix [num_nodes, in_features]
|
|
199
|
+
edges INTEGER[][], -- Edge list [[src, dst], ...] (0-indexed)
|
|
200
|
+
num_heads INTEGER DEFAULT 4,
|
|
201
|
+
attention_dim INTEGER DEFAULT 8
|
|
202
|
+
) RETURNS TABLE (
|
|
203
|
+
output_features REAL[][],
|
|
204
|
+
attention_coefficients REAL[][]
|
|
205
|
+
) AS $$
|
|
206
|
+
DECLARE
|
|
207
|
+
num_nodes INTEGER;
|
|
208
|
+
in_features INTEGER;
|
|
209
|
+
out_features INTEGER;
|
|
210
|
+
head_outputs REAL[][][];
|
|
211
|
+
all_attention REAL[][];
|
|
212
|
+
h INTEGER;
|
|
213
|
+
i INTEGER;
|
|
214
|
+
j INTEGER;
|
|
215
|
+
src INTEGER;
|
|
216
|
+
dst INTEGER;
|
|
217
|
+
edge_idx INTEGER;
|
|
218
|
+
attention_scores REAL[];
|
|
219
|
+
attention_weights REAL[];
|
|
220
|
+
neighbor_indices INTEGER[];
|
|
221
|
+
weighted_sum REAL[];
|
|
222
|
+
final_output REAL[][];
|
|
223
|
+
BEGIN
|
|
224
|
+
-- Get dimensions
|
|
225
|
+
num_nodes := array_length(nodes, 1);
|
|
226
|
+
in_features := array_length(nodes[1], 1);
|
|
227
|
+
out_features := attention_dim * num_heads;
|
|
228
|
+
|
|
229
|
+
-- Initialize outputs
|
|
230
|
+
head_outputs := ARRAY[]::REAL[][][];
|
|
231
|
+
all_attention := ARRAY[]::REAL[][];
|
|
232
|
+
|
|
233
|
+
-- Build adjacency list
|
|
234
|
+
DECLARE
|
|
235
|
+
adj_list INTEGER[][];
|
|
236
|
+
BEGIN
|
|
237
|
+
adj_list := ARRAY[]::INTEGER[][];
|
|
238
|
+
FOR i IN 1..num_nodes LOOP
|
|
239
|
+
adj_list := array_cat(adj_list, ARRAY[ARRAY[i]::INTEGER[]]); -- Self-loop
|
|
240
|
+
END LOOP;
|
|
241
|
+
|
|
242
|
+
FOR edge_idx IN 1..array_length(edges, 1) LOOP
|
|
243
|
+
src := edges[edge_idx][1] + 1;
|
|
244
|
+
dst := edges[edge_idx][2] + 1;
|
|
245
|
+
IF dst >= 1 AND dst <= num_nodes AND src >= 1 AND src <= num_nodes THEN
|
|
246
|
+
adj_list[dst] := array_append(adj_list[dst], src);
|
|
247
|
+
END IF;
|
|
248
|
+
END LOOP;
|
|
249
|
+
|
|
250
|
+
-- Process each attention head
|
|
251
|
+
FOR h IN 1..num_heads LOOP
|
|
252
|
+
DECLARE
|
|
253
|
+
head_result REAL[][];
|
|
254
|
+
BEGIN
|
|
255
|
+
head_result := ARRAY[]::REAL[][];
|
|
256
|
+
|
|
257
|
+
FOR i IN 1..num_nodes LOOP
|
|
258
|
+
neighbor_indices := adj_list[i];
|
|
259
|
+
|
|
260
|
+
-- Compute attention scores
|
|
261
|
+
attention_scores := ARRAY[]::REAL[];
|
|
262
|
+
FOR j IN 1..array_length(neighbor_indices, 1) LOOP
|
|
263
|
+
DECLARE
|
|
264
|
+
neighbor_idx INTEGER := neighbor_indices[j];
|
|
265
|
+
score REAL := 0;
|
|
266
|
+
k INTEGER;
|
|
267
|
+
BEGIN
|
|
268
|
+
-- Simple dot-product attention
|
|
269
|
+
FOR k IN 1..LEAST(in_features, attention_dim) LOOP
|
|
270
|
+
score := score + nodes[i][k] * nodes[neighbor_idx][k];
|
|
271
|
+
END LOOP;
|
|
272
|
+
score := score / sqrt(attention_dim::REAL);
|
|
273
|
+
attention_scores := array_append(attention_scores, score);
|
|
274
|
+
END;
|
|
275
|
+
END LOOP;
|
|
276
|
+
|
|
277
|
+
-- Apply LeakyReLU and softmax
|
|
278
|
+
attention_scores := claude_flow.leaky_relu(attention_scores, 0.2);
|
|
279
|
+
attention_weights := claude_flow.softmax(attention_scores);
|
|
280
|
+
|
|
281
|
+
-- Store attention for debugging
|
|
282
|
+
all_attention := array_cat(all_attention, ARRAY[attention_weights]);
|
|
283
|
+
|
|
284
|
+
-- Compute weighted sum of neighbor features
|
|
285
|
+
weighted_sum := ARRAY[]::REAL[];
|
|
286
|
+
FOR j IN 1..LEAST(in_features, attention_dim) LOOP
|
|
287
|
+
DECLARE
|
|
288
|
+
sum_val REAL := 0;
|
|
289
|
+
k INTEGER;
|
|
290
|
+
BEGIN
|
|
291
|
+
FOR k IN 1..array_length(neighbor_indices, 1) LOOP
|
|
292
|
+
sum_val := sum_val + attention_weights[k] * nodes[neighbor_indices[k]][j];
|
|
293
|
+
END LOOP;
|
|
294
|
+
weighted_sum := array_append(weighted_sum, sum_val);
|
|
295
|
+
END;
|
|
296
|
+
END LOOP;
|
|
297
|
+
|
|
298
|
+
-- Apply activation
|
|
299
|
+
weighted_sum := claude_flow.relu(weighted_sum);
|
|
300
|
+
head_result := array_cat(head_result, ARRAY[weighted_sum]);
|
|
301
|
+
END LOOP;
|
|
302
|
+
|
|
303
|
+
head_outputs := array_cat(head_outputs, ARRAY[head_result]);
|
|
304
|
+
END;
|
|
305
|
+
END LOOP;
|
|
306
|
+
|
|
307
|
+
-- Concatenate head outputs
|
|
308
|
+
final_output := ARRAY[]::REAL[][];
|
|
309
|
+
FOR i IN 1..num_nodes LOOP
|
|
310
|
+
DECLARE
|
|
311
|
+
concat_features REAL[] := ARRAY[]::REAL[];
|
|
312
|
+
BEGIN
|
|
313
|
+
FOR h IN 1..num_heads LOOP
|
|
314
|
+
FOR j IN 1..array_length(head_outputs[h][i], 1) LOOP
|
|
315
|
+
concat_features := array_append(concat_features, head_outputs[h][i][j]);
|
|
316
|
+
END LOOP;
|
|
317
|
+
END LOOP;
|
|
318
|
+
final_output := array_cat(final_output, ARRAY[concat_features]);
|
|
319
|
+
END;
|
|
320
|
+
END LOOP;
|
|
321
|
+
|
|
322
|
+
output_features := final_output;
|
|
323
|
+
attention_coefficients := all_attention;
|
|
324
|
+
RETURN NEXT;
|
|
325
|
+
END;
|
|
326
|
+
END;
|
|
327
|
+
$$ LANGUAGE plpgsql STABLE;
|
|
328
|
+
|
|
329
|
+
-- ----------------------------------------------------------------------------
|
|
330
|
+
-- GraphSAGE Layer
|
|
331
|
+
-- h_v = σ(W * CONCAT(h_v, AGG({h_u : u ∈ N(v)})))
|
|
332
|
+
-- Aggregation: mean, max, or LSTM
|
|
333
|
+
-- ----------------------------------------------------------------------------
|
|
334
|
+
|
|
335
|
+
CREATE OR REPLACE FUNCTION claude_flow.graph_sage(
|
|
336
|
+
nodes REAL[][], -- Node feature matrix [num_nodes, in_features]
|
|
337
|
+
edges INTEGER[][], -- Edge list [[src, dst], ...] (0-indexed)
|
|
338
|
+
aggregation TEXT DEFAULT 'mean' -- Aggregation: mean, max, pool
|
|
339
|
+
) RETURNS TABLE (
|
|
340
|
+
output_features REAL[][],
|
|
341
|
+
sampled_neighbors INTEGER[][]
|
|
342
|
+
) AS $$
|
|
343
|
+
DECLARE
|
|
344
|
+
num_nodes INTEGER;
|
|
345
|
+
in_features INTEGER;
|
|
346
|
+
adj_list INTEGER[][];
|
|
347
|
+
aggregated REAL[][];
|
|
348
|
+
final_output REAL[][];
|
|
349
|
+
i INTEGER;
|
|
350
|
+
j INTEGER;
|
|
351
|
+
src INTEGER;
|
|
352
|
+
dst INTEGER;
|
|
353
|
+
edge_idx INTEGER;
|
|
354
|
+
neighbors INTEGER[];
|
|
355
|
+
neighbor_features REAL[][];
|
|
356
|
+
agg_result REAL[];
|
|
357
|
+
BEGIN
|
|
358
|
+
-- Get dimensions
|
|
359
|
+
num_nodes := array_length(nodes, 1);
|
|
360
|
+
in_features := array_length(nodes[1], 1);
|
|
361
|
+
|
|
362
|
+
-- Build adjacency list
|
|
363
|
+
adj_list := ARRAY[]::INTEGER[][];
|
|
364
|
+
FOR i IN 1..num_nodes LOOP
|
|
365
|
+
adj_list := array_cat(adj_list, ARRAY[ARRAY[]::INTEGER[]]);
|
|
366
|
+
END LOOP;
|
|
367
|
+
|
|
368
|
+
FOR edge_idx IN 1..array_length(edges, 1) LOOP
|
|
369
|
+
src := edges[edge_idx][1] + 1;
|
|
370
|
+
dst := edges[edge_idx][2] + 1;
|
|
371
|
+
IF dst >= 1 AND dst <= num_nodes AND src >= 1 AND src <= num_nodes THEN
|
|
372
|
+
adj_list[dst] := array_append(adj_list[dst], src);
|
|
373
|
+
END IF;
|
|
374
|
+
END LOOP;
|
|
375
|
+
|
|
376
|
+
-- Aggregate neighbor features
|
|
377
|
+
aggregated := ARRAY[]::REAL[][];
|
|
378
|
+
|
|
379
|
+
FOR i IN 1..num_nodes LOOP
|
|
380
|
+
neighbors := adj_list[i];
|
|
381
|
+
|
|
382
|
+
IF array_length(neighbors, 1) IS NULL OR array_length(neighbors, 1) = 0 THEN
|
|
383
|
+
-- No neighbors, use self
|
|
384
|
+
aggregated := array_cat(aggregated, ARRAY[nodes[i]]);
|
|
385
|
+
ELSE
|
|
386
|
+
-- Collect neighbor features
|
|
387
|
+
neighbor_features := ARRAY[]::REAL[][];
|
|
388
|
+
FOR j IN 1..array_length(neighbors, 1) LOOP
|
|
389
|
+
neighbor_features := array_cat(neighbor_features, ARRAY[nodes[neighbors[j]]]);
|
|
390
|
+
END LOOP;
|
|
391
|
+
|
|
392
|
+
-- Aggregate based on method
|
|
393
|
+
agg_result := ARRAY[]::REAL[];
|
|
394
|
+
|
|
395
|
+
CASE aggregation
|
|
396
|
+
WHEN 'mean' THEN
|
|
397
|
+
FOR j IN 1..in_features LOOP
|
|
398
|
+
DECLARE
|
|
399
|
+
mean_val REAL := 0;
|
|
400
|
+
k INTEGER;
|
|
401
|
+
BEGIN
|
|
402
|
+
FOR k IN 1..array_length(neighbor_features, 1) LOOP
|
|
403
|
+
mean_val := mean_val + neighbor_features[k][j];
|
|
404
|
+
END LOOP;
|
|
405
|
+
agg_result := array_append(agg_result, mean_val / array_length(neighbor_features, 1));
|
|
406
|
+
END;
|
|
407
|
+
END LOOP;
|
|
408
|
+
|
|
409
|
+
WHEN 'max' THEN
|
|
410
|
+
FOR j IN 1..in_features LOOP
|
|
411
|
+
DECLARE
|
|
412
|
+
max_val REAL := neighbor_features[1][j];
|
|
413
|
+
k INTEGER;
|
|
414
|
+
BEGIN
|
|
415
|
+
FOR k IN 2..array_length(neighbor_features, 1) LOOP
|
|
416
|
+
IF neighbor_features[k][j] > max_val THEN
|
|
417
|
+
max_val := neighbor_features[k][j];
|
|
418
|
+
END IF;
|
|
419
|
+
END LOOP;
|
|
420
|
+
agg_result := array_append(agg_result, max_val);
|
|
421
|
+
END;
|
|
422
|
+
END LOOP;
|
|
423
|
+
|
|
424
|
+
WHEN 'pool' THEN
|
|
425
|
+
-- Max-pooling after element-wise transformation
|
|
426
|
+
FOR j IN 1..in_features LOOP
|
|
427
|
+
DECLARE
|
|
428
|
+
max_val REAL := GREATEST(0, neighbor_features[1][j]); -- ReLU
|
|
429
|
+
k INTEGER;
|
|
430
|
+
BEGIN
|
|
431
|
+
FOR k IN 2..array_length(neighbor_features, 1) LOOP
|
|
432
|
+
IF GREATEST(0, neighbor_features[k][j]) > max_val THEN
|
|
433
|
+
max_val := GREATEST(0, neighbor_features[k][j]);
|
|
434
|
+
END IF;
|
|
435
|
+
END LOOP;
|
|
436
|
+
agg_result := array_append(agg_result, max_val);
|
|
437
|
+
END;
|
|
438
|
+
END LOOP;
|
|
439
|
+
|
|
440
|
+
ELSE
|
|
441
|
+
-- Default to mean
|
|
442
|
+
FOR j IN 1..in_features LOOP
|
|
443
|
+
DECLARE
|
|
444
|
+
mean_val REAL := 0;
|
|
445
|
+
k INTEGER;
|
|
446
|
+
BEGIN
|
|
447
|
+
FOR k IN 1..array_length(neighbor_features, 1) LOOP
|
|
448
|
+
mean_val := mean_val + neighbor_features[k][j];
|
|
449
|
+
END LOOP;
|
|
450
|
+
agg_result := array_append(agg_result, mean_val / array_length(neighbor_features, 1));
|
|
451
|
+
END;
|
|
452
|
+
END LOOP;
|
|
453
|
+
END CASE;
|
|
454
|
+
|
|
455
|
+
aggregated := array_cat(aggregated, ARRAY[agg_result]);
|
|
456
|
+
END IF;
|
|
457
|
+
END LOOP;
|
|
458
|
+
|
|
459
|
+
-- Concatenate self features with aggregated neighbor features and apply activation
|
|
460
|
+
final_output := ARRAY[]::REAL[][];
|
|
461
|
+
FOR i IN 1..num_nodes LOOP
|
|
462
|
+
DECLARE
|
|
463
|
+
concat_features REAL[];
|
|
464
|
+
BEGIN
|
|
465
|
+
-- Concatenate: [self || aggregated]
|
|
466
|
+
concat_features := nodes[i] || aggregated[i];
|
|
467
|
+
-- Apply ReLU activation
|
|
468
|
+
concat_features := claude_flow.relu(concat_features);
|
|
469
|
+
-- Normalize
|
|
470
|
+
concat_features := claude_flow.l2_normalize(concat_features);
|
|
471
|
+
final_output := array_cat(final_output, ARRAY[concat_features]);
|
|
472
|
+
END;
|
|
473
|
+
END LOOP;
|
|
474
|
+
|
|
475
|
+
output_features := final_output;
|
|
476
|
+
sampled_neighbors := adj_list;
|
|
477
|
+
RETURN NEXT;
|
|
478
|
+
END;
|
|
479
|
+
$$ LANGUAGE plpgsql STABLE;
|
|
480
|
+
|
|
481
|
+
-- ----------------------------------------------------------------------------
|
|
482
|
+
-- Message Passing Neural Network (MPNN) - Generic Framework
|
|
483
|
+
-- ----------------------------------------------------------------------------
|
|
484
|
+
|
|
485
|
+
CREATE OR REPLACE FUNCTION claude_flow.mpnn_layer(
|
|
486
|
+
nodes REAL[][], -- Node features
|
|
487
|
+
edges INTEGER[][], -- Edge list
|
|
488
|
+
edge_features REAL[][] DEFAULT NULL, -- Optional edge features
|
|
489
|
+
message_fn TEXT DEFAULT 'concat', -- concat, add, mul
|
|
490
|
+
aggregate_fn TEXT DEFAULT 'sum' -- sum, mean, max
|
|
491
|
+
) RETURNS REAL[][] AS $$
|
|
492
|
+
DECLARE
|
|
493
|
+
num_nodes INTEGER;
|
|
494
|
+
in_features INTEGER;
|
|
495
|
+
edge_dim INTEGER;
|
|
496
|
+
messages REAL[][];
|
|
497
|
+
aggregated REAL[][];
|
|
498
|
+
i INTEGER;
|
|
499
|
+
j INTEGER;
|
|
500
|
+
src INTEGER;
|
|
501
|
+
dst INTEGER;
|
|
502
|
+
edge_idx INTEGER;
|
|
503
|
+
BEGIN
|
|
504
|
+
num_nodes := array_length(nodes, 1);
|
|
505
|
+
in_features := array_length(nodes[1], 1);
|
|
506
|
+
|
|
507
|
+
IF edge_features IS NOT NULL THEN
|
|
508
|
+
edge_dim := array_length(edge_features[1], 1);
|
|
509
|
+
ELSE
|
|
510
|
+
edge_dim := 0;
|
|
511
|
+
END IF;
|
|
512
|
+
|
|
513
|
+
-- Initialize aggregated features
|
|
514
|
+
aggregated := ARRAY[]::REAL[][];
|
|
515
|
+
FOR i IN 1..num_nodes LOOP
|
|
516
|
+
DECLARE
|
|
517
|
+
zero_features REAL[] := ARRAY[]::REAL[];
|
|
518
|
+
BEGIN
|
|
519
|
+
FOR j IN 1..in_features LOOP
|
|
520
|
+
zero_features := array_append(zero_features, 0.0);
|
|
521
|
+
END LOOP;
|
|
522
|
+
aggregated := array_cat(aggregated, ARRAY[zero_features]);
|
|
523
|
+
END;
|
|
524
|
+
END LOOP;
|
|
525
|
+
|
|
526
|
+
-- Compute and aggregate messages
|
|
527
|
+
DECLARE
|
|
528
|
+
neighbor_counts INTEGER[] := ARRAY[]::INTEGER[];
|
|
529
|
+
BEGIN
|
|
530
|
+
FOR i IN 1..num_nodes LOOP
|
|
531
|
+
neighbor_counts := array_append(neighbor_counts, 0);
|
|
532
|
+
END LOOP;
|
|
533
|
+
|
|
534
|
+
FOR edge_idx IN 1..array_length(edges, 1) LOOP
|
|
535
|
+
src := edges[edge_idx][1] + 1;
|
|
536
|
+
dst := edges[edge_idx][2] + 1;
|
|
537
|
+
|
|
538
|
+
IF src >= 1 AND src <= num_nodes AND dst >= 1 AND dst <= num_nodes THEN
|
|
539
|
+
neighbor_counts[dst] := neighbor_counts[dst] + 1;
|
|
540
|
+
|
|
541
|
+
-- Compute message
|
|
542
|
+
DECLARE
|
|
543
|
+
message REAL[] := nodes[src];
|
|
544
|
+
BEGIN
|
|
545
|
+
-- Apply message function
|
|
546
|
+
CASE message_fn
|
|
547
|
+
WHEN 'concat' THEN
|
|
548
|
+
IF edge_features IS NOT NULL THEN
|
|
549
|
+
message := message || edge_features[edge_idx];
|
|
550
|
+
END IF;
|
|
551
|
+
WHEN 'mul' THEN
|
|
552
|
+
IF edge_features IS NOT NULL THEN
|
|
553
|
+
FOR j IN 1..LEAST(in_features, edge_dim) LOOP
|
|
554
|
+
message[j] := message[j] * edge_features[edge_idx][j];
|
|
555
|
+
END LOOP;
|
|
556
|
+
END IF;
|
|
557
|
+
ELSE
|
|
558
|
+
NULL; -- Default: just use source features
|
|
559
|
+
END CASE;
|
|
560
|
+
|
|
561
|
+
-- Aggregate
|
|
562
|
+
FOR j IN 1..LEAST(array_length(message, 1), in_features) LOOP
|
|
563
|
+
CASE aggregate_fn
|
|
564
|
+
WHEN 'sum' THEN
|
|
565
|
+
aggregated[dst][j] := aggregated[dst][j] + message[j];
|
|
566
|
+
WHEN 'max' THEN
|
|
567
|
+
IF message[j] > aggregated[dst][j] THEN
|
|
568
|
+
aggregated[dst][j] := message[j];
|
|
569
|
+
END IF;
|
|
570
|
+
ELSE -- mean (accumulated, divide later)
|
|
571
|
+
aggregated[dst][j] := aggregated[dst][j] + message[j];
|
|
572
|
+
END CASE;
|
|
573
|
+
END LOOP;
|
|
574
|
+
END;
|
|
575
|
+
END IF;
|
|
576
|
+
END LOOP;
|
|
577
|
+
|
|
578
|
+
-- Finalize mean aggregation
|
|
579
|
+
IF aggregate_fn = 'mean' THEN
|
|
580
|
+
FOR i IN 1..num_nodes LOOP
|
|
581
|
+
IF neighbor_counts[i] > 0 THEN
|
|
582
|
+
FOR j IN 1..in_features LOOP
|
|
583
|
+
aggregated[i][j] := aggregated[i][j] / neighbor_counts[i];
|
|
584
|
+
END LOOP;
|
|
585
|
+
END IF;
|
|
586
|
+
END LOOP;
|
|
587
|
+
END IF;
|
|
588
|
+
END;
|
|
589
|
+
|
|
590
|
+
-- Update: combine self with aggregated
|
|
591
|
+
FOR i IN 1..num_nodes LOOP
|
|
592
|
+
FOR j IN 1..in_features LOOP
|
|
593
|
+
aggregated[i][j] := aggregated[i][j] + nodes[i][j];
|
|
594
|
+
END LOOP;
|
|
595
|
+
aggregated[i] := claude_flow.relu(aggregated[i]);
|
|
596
|
+
END LOOP;
|
|
597
|
+
|
|
598
|
+
RETURN aggregated;
|
|
599
|
+
END;
|
|
600
|
+
$$ LANGUAGE plpgsql STABLE;
|
|
601
|
+
|
|
602
|
+
-- ----------------------------------------------------------------------------
|
|
603
|
+
-- GNN Cache Management
|
|
604
|
+
-- ----------------------------------------------------------------------------
|
|
605
|
+
|
|
606
|
+
-- Cache GNN layer output
|
|
607
|
+
CREATE OR REPLACE FUNCTION claude_flow.cache_gnn_result(
|
|
608
|
+
p_graph_hash TEXT,
|
|
609
|
+
p_layer_type TEXT,
|
|
610
|
+
p_layer_index INTEGER,
|
|
611
|
+
p_num_nodes INTEGER,
|
|
612
|
+
p_num_edges INTEGER,
|
|
613
|
+
p_node_features_dim INTEGER,
|
|
614
|
+
p_node_embeddings REAL[],
|
|
615
|
+
p_output_dim INTEGER,
|
|
616
|
+
p_aggregation TEXT DEFAULT 'mean',
|
|
617
|
+
p_num_heads INTEGER DEFAULT 1,
|
|
618
|
+
p_ttl_hours INTEGER DEFAULT 24
|
|
619
|
+
) RETURNS UUID AS $$
|
|
620
|
+
DECLARE
|
|
621
|
+
v_cache_key TEXT;
|
|
622
|
+
v_id UUID;
|
|
623
|
+
BEGIN
|
|
624
|
+
v_cache_key := md5(p_graph_hash || p_layer_type || p_layer_index::TEXT);
|
|
625
|
+
|
|
626
|
+
INSERT INTO claude_flow.gnn_cache (
|
|
627
|
+
cache_key,
|
|
628
|
+
graph_hash,
|
|
629
|
+
layer_type,
|
|
630
|
+
layer_index,
|
|
631
|
+
num_nodes,
|
|
632
|
+
num_edges,
|
|
633
|
+
node_features_dim,
|
|
634
|
+
node_embeddings,
|
|
635
|
+
output_dim,
|
|
636
|
+
aggregation,
|
|
637
|
+
num_heads,
|
|
638
|
+
expires_at
|
|
639
|
+
) VALUES (
|
|
640
|
+
v_cache_key,
|
|
641
|
+
p_graph_hash,
|
|
642
|
+
p_layer_type,
|
|
643
|
+
p_layer_index,
|
|
644
|
+
p_num_nodes,
|
|
645
|
+
p_num_edges,
|
|
646
|
+
p_node_features_dim,
|
|
647
|
+
p_node_embeddings,
|
|
648
|
+
p_output_dim,
|
|
649
|
+
p_aggregation,
|
|
650
|
+
p_num_heads,
|
|
651
|
+
NOW() + (p_ttl_hours || ' hours')::INTERVAL
|
|
652
|
+
)
|
|
653
|
+
ON CONFLICT (cache_key) DO UPDATE
|
|
654
|
+
SET node_embeddings = EXCLUDED.node_embeddings,
|
|
655
|
+
hit_count = claude_flow.gnn_cache.hit_count + 1,
|
|
656
|
+
last_accessed_at = NOW(),
|
|
657
|
+
expires_at = EXCLUDED.expires_at
|
|
658
|
+
RETURNING id INTO v_id;
|
|
659
|
+
|
|
660
|
+
RETURN v_id;
|
|
661
|
+
END;
|
|
662
|
+
$$ LANGUAGE plpgsql;
|
|
663
|
+
|
|
664
|
+
-- Retrieve cached GNN result
|
|
665
|
+
CREATE OR REPLACE FUNCTION claude_flow.get_cached_gnn(
|
|
666
|
+
p_graph_hash TEXT,
|
|
667
|
+
p_layer_type TEXT,
|
|
668
|
+
p_layer_index INTEGER
|
|
669
|
+
) RETURNS TABLE (
|
|
670
|
+
node_embeddings REAL[],
|
|
671
|
+
output_dim INTEGER,
|
|
672
|
+
cache_hit BOOLEAN
|
|
673
|
+
) AS $$
|
|
674
|
+
DECLARE
|
|
675
|
+
v_cache_key TEXT;
|
|
676
|
+
v_record RECORD;
|
|
677
|
+
BEGIN
|
|
678
|
+
v_cache_key := md5(p_graph_hash || p_layer_type || p_layer_index::TEXT);
|
|
679
|
+
|
|
680
|
+
SELECT gc.node_embeddings, gc.output_dim
|
|
681
|
+
INTO v_record
|
|
682
|
+
FROM claude_flow.gnn_cache gc
|
|
683
|
+
WHERE gc.cache_key = v_cache_key
|
|
684
|
+
AND (gc.expires_at IS NULL OR gc.expires_at > NOW());
|
|
685
|
+
|
|
686
|
+
IF FOUND THEN
|
|
687
|
+
UPDATE claude_flow.gnn_cache
|
|
688
|
+
SET hit_count = hit_count + 1,
|
|
689
|
+
last_accessed_at = NOW()
|
|
690
|
+
WHERE cache_key = v_cache_key;
|
|
691
|
+
|
|
692
|
+
node_embeddings := v_record.node_embeddings;
|
|
693
|
+
output_dim := v_record.output_dim;
|
|
694
|
+
cache_hit := TRUE;
|
|
695
|
+
ELSE
|
|
696
|
+
node_embeddings := NULL;
|
|
697
|
+
output_dim := NULL;
|
|
698
|
+
cache_hit := FALSE;
|
|
699
|
+
END IF;
|
|
700
|
+
|
|
701
|
+
RETURN NEXT;
|
|
702
|
+
END;
|
|
703
|
+
$$ LANGUAGE plpgsql;
|
|
704
|
+
|
|
705
|
+
-- ----------------------------------------------------------------------------
|
|
706
|
+
-- Record migration
|
|
707
|
+
-- ----------------------------------------------------------------------------
|
|
708
|
+
INSERT INTO claude_flow.migrations (name, checksum)
|
|
709
|
+
VALUES ('006_create_gnn_functions', md5('006_create_gnn_functions'))
|
|
710
|
+
ON CONFLICT (name) DO NOTHING;
|
|
711
|
+
|
|
712
|
+
COMMIT;
|
|
713
|
+
|
|
714
|
+
-- ============================================================================
|
|
715
|
+
-- Rollback Script
|
|
716
|
+
-- ============================================================================
|
|
717
|
+
-- BEGIN;
|
|
718
|
+
-- DROP FUNCTION IF EXISTS claude_flow.get_cached_gnn(TEXT, TEXT, INTEGER);
|
|
719
|
+
-- DROP FUNCTION IF EXISTS claude_flow.cache_gnn_result(TEXT, TEXT, INTEGER, INTEGER, INTEGER, INTEGER, REAL[], INTEGER, TEXT, INTEGER, INTEGER);
|
|
720
|
+
-- DROP FUNCTION IF EXISTS claude_flow.mpnn_layer(REAL[][], INTEGER[][], REAL[][], TEXT, TEXT);
|
|
721
|
+
-- DROP FUNCTION IF EXISTS claude_flow.graph_sage(REAL[][], INTEGER[][], TEXT);
|
|
722
|
+
-- DROP FUNCTION IF EXISTS claude_flow.gat_layer(REAL[][], INTEGER[][], INTEGER, INTEGER);
|
|
723
|
+
-- DROP FUNCTION IF EXISTS claude_flow.gcn_layer(REAL[][], INTEGER[][], REAL[][]);
|
|
724
|
+
-- DROP FUNCTION IF EXISTS claude_flow.matmul(REAL[][], REAL[]);
|
|
725
|
+
-- DROP FUNCTION IF EXISTS claude_flow.leaky_relu(REAL[], REAL);
|
|
726
|
+
-- DROP FUNCTION IF EXISTS claude_flow.relu(REAL[]);
|
|
727
|
+
-- DELETE FROM claude_flow.migrations WHERE name = '006_create_gnn_functions';
|
|
728
|
+
-- COMMIT;
|