agentic-qe 3.8.11 → 3.8.13
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/.claude/skills/qe-code-intelligence/SKILL.md +29 -20
- package/.claude/skills/qe-code-intelligence/evals/qe-code-intelligence.yaml +3 -3
- package/.claude/skills/qe-quality-assessment/SKILL.md +1 -1
- package/.claude/skills/qe-test-generation/SKILL.md +1 -1
- package/.claude/skills/skills-manifest.json +1 -1
- package/CHANGELOG.md +45 -0
- package/README.md +9 -0
- package/assets/skills/qe-code-intelligence/SKILL.md +29 -20
- package/assets/skills/qe-code-intelligence/evals/qe-code-intelligence.yaml +3 -3
- package/assets/skills/qe-quality-assessment/SKILL.md +1 -1
- package/assets/skills/qe-test-generation/SKILL.md +1 -1
- package/dist/cli/bundle.js +1162 -1046
- package/dist/cli/commands/code.js +149 -11
- package/dist/cli/commands/init.js +3 -2
- package/dist/cli/commands/ruvector-commands.js +17 -0
- package/dist/cli/handlers/init-handler.d.ts +1 -0
- package/dist/cli/handlers/init-handler.js +15 -10
- package/dist/cli/utils/file-discovery.d.ts +1 -0
- package/dist/cli/utils/file-discovery.js +1 -1
- package/dist/domains/code-intelligence/coordinator-gnn.d.ts +21 -0
- package/dist/domains/code-intelligence/coordinator-gnn.js +102 -0
- package/dist/domains/contract-testing/coordinator.js +13 -0
- package/dist/domains/coverage-analysis/coordinator.js +5 -0
- package/dist/domains/defect-intelligence/coordinator.d.ts +1 -0
- package/dist/domains/defect-intelligence/coordinator.js +43 -0
- package/dist/domains/quality-assessment/coordinator.js +26 -0
- package/dist/domains/test-generation/coordinator.js +14 -0
- package/dist/init/orchestrator.js +1 -0
- package/dist/init/phases/08-mcp.js +4 -4
- package/dist/init/phases/phase-interface.d.ts +3 -1
- package/dist/integrations/agentic-flow/reasoning-bank/experience-replay.d.ts +11 -0
- package/dist/integrations/agentic-flow/reasoning-bank/experience-replay.js +44 -1
- package/dist/integrations/rl-suite/algorithms/eprop.d.ts +79 -0
- package/dist/integrations/rl-suite/algorithms/eprop.js +284 -0
- package/dist/integrations/rl-suite/algorithms/index.d.ts +2 -1
- package/dist/integrations/rl-suite/algorithms/index.js +2 -1
- package/dist/integrations/rl-suite/index.d.ts +2 -2
- package/dist/integrations/rl-suite/index.js +2 -2
- package/dist/integrations/rl-suite/interfaces.d.ts +3 -3
- package/dist/integrations/rl-suite/interfaces.js +1 -1
- package/dist/integrations/rl-suite/orchestrator.d.ts +2 -2
- package/dist/integrations/rl-suite/orchestrator.js +3 -2
- package/dist/integrations/rl-suite/reward-signals.d.ts +1 -1
- package/dist/integrations/rl-suite/reward-signals.js +1 -1
- package/dist/integrations/ruvector/coherence-gate-cohomology.d.ts +41 -0
- package/dist/integrations/ruvector/coherence-gate-cohomology.js +47 -0
- package/dist/integrations/ruvector/coherence-gate-core.d.ts +200 -0
- package/dist/integrations/ruvector/coherence-gate-core.js +294 -0
- package/dist/integrations/ruvector/coherence-gate-energy.d.ts +136 -0
- package/dist/integrations/ruvector/coherence-gate-energy.js +373 -0
- package/dist/integrations/ruvector/coherence-gate-vector.d.ts +38 -0
- package/dist/integrations/ruvector/coherence-gate-vector.js +76 -0
- package/dist/integrations/ruvector/coherence-gate.d.ts +10 -311
- package/dist/integrations/ruvector/coherence-gate.js +10 -652
- package/dist/integrations/ruvector/cold-tier-trainer.d.ts +103 -0
- package/dist/integrations/ruvector/cold-tier-trainer.js +377 -0
- package/dist/integrations/ruvector/cusum-detector.d.ts +70 -0
- package/dist/integrations/ruvector/cusum-detector.js +142 -0
- package/dist/integrations/ruvector/delta-tracker.d.ts +122 -0
- package/dist/integrations/ruvector/delta-tracker.js +311 -0
- package/dist/integrations/ruvector/domain-transfer.d.ts +79 -1
- package/dist/integrations/ruvector/domain-transfer.js +158 -2
- package/dist/integrations/ruvector/eprop-learner.d.ts +135 -0
- package/dist/integrations/ruvector/eprop-learner.js +351 -0
- package/dist/integrations/ruvector/feature-flags.d.ts +177 -0
- package/dist/integrations/ruvector/feature-flags.js +145 -0
- package/dist/integrations/ruvector/graphmae-encoder.d.ts +88 -0
- package/dist/integrations/ruvector/graphmae-encoder.js +360 -0
- package/dist/integrations/ruvector/hdc-fingerprint.d.ts +127 -0
- package/dist/integrations/ruvector/hdc-fingerprint.js +222 -0
- package/dist/integrations/ruvector/hopfield-memory.d.ts +97 -0
- package/dist/integrations/ruvector/hopfield-memory.js +238 -0
- package/dist/integrations/ruvector/index.d.ts +13 -2
- package/dist/integrations/ruvector/index.js +46 -2
- package/dist/integrations/ruvector/mincut-wrapper.d.ts +7 -0
- package/dist/integrations/ruvector/mincut-wrapper.js +54 -2
- package/dist/integrations/ruvector/reservoir-replay.d.ts +172 -0
- package/dist/integrations/ruvector/reservoir-replay.js +335 -0
- package/dist/integrations/ruvector/solver-adapter.d.ts +93 -0
- package/dist/integrations/ruvector/solver-adapter.js +299 -0
- package/dist/integrations/ruvector/sona-persistence.d.ts +33 -0
- package/dist/integrations/ruvector/sona-persistence.js +47 -0
- package/dist/integrations/ruvector/spectral-sparsifier.d.ts +154 -0
- package/dist/integrations/ruvector/spectral-sparsifier.js +389 -0
- package/dist/integrations/ruvector/temporal-causality.d.ts +63 -0
- package/dist/integrations/ruvector/temporal-causality.js +317 -0
- package/dist/learning/pattern-promotion.d.ts +63 -0
- package/dist/learning/pattern-promotion.js +235 -1
- package/dist/learning/pattern-store.d.ts +2 -0
- package/dist/learning/pattern-store.js +187 -1
- package/dist/learning/sqlite-persistence.d.ts +2 -0
- package/dist/learning/sqlite-persistence.js +4 -0
- package/dist/mcp/bundle.js +506 -427
- package/dist/shared/utils/index.d.ts +1 -0
- package/dist/shared/utils/index.js +1 -0
- package/dist/shared/utils/xorshift128.d.ts +24 -0
- package/dist/shared/utils/xorshift128.js +50 -0
- package/package.json +1 -1
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spectral Graph Sparsification (ADR-087, Milestone 3, R9)
|
|
3
|
+
*
|
|
4
|
+
* Spectral graph sparsifier using degree-based leverage score sampling.
|
|
5
|
+
*
|
|
6
|
+
* Compresses weighted undirected graphs while approximately preserving
|
|
7
|
+
* Laplacian spectral properties within a (1 +/- epsilon) factor.
|
|
8
|
+
*
|
|
9
|
+
* Algorithm overview:
|
|
10
|
+
* 1. Approximate leverage scores via degree-based heuristic:
|
|
11
|
+
* leverage(u,v) ≈ w(u,v) * (1/deg_w(u) + 1/deg_w(v)).
|
|
12
|
+
* This is a practical O(m) approximation — NOT true effective
|
|
13
|
+
* resistance (which requires the Laplacian pseudoinverse, O(n^3)).
|
|
14
|
+
* 2. Sample each edge with probability proportional to its leverage
|
|
15
|
+
* score, scaled to a target edge budget.
|
|
16
|
+
* 3. Rescale surviving edge weights by 1/p_e to keep expectations unbiased.
|
|
17
|
+
* 4. Validate by comparing top-k Laplacian eigenvalues of both graphs.
|
|
18
|
+
*
|
|
19
|
+
* Limitations: The degree-based heuristic can misjudge edges between
|
|
20
|
+
* high-degree nodes (retained when redundant) and low-degree bridges
|
|
21
|
+
* (dropped when structurally important). For production use on critical
|
|
22
|
+
* graphs, upgrade to JL-projected effective resistance or spanning-tree
|
|
23
|
+
* sampling when @ruvector/sparsifier-wasm becomes available.
|
|
24
|
+
*
|
|
25
|
+
* The implementation is fully synchronous --- matrix operations are fast
|
|
26
|
+
* enough in TypeScript for graphs up to ~10K nodes.
|
|
27
|
+
*
|
|
28
|
+
* @module integrations/ruvector/spectral-sparsifier
|
|
29
|
+
*/
|
|
30
|
+
// ============================================================================
|
|
31
|
+
// Seeded PRNG (Mulberry32)
|
|
32
|
+
// ============================================================================
|
|
33
|
+
/**
|
|
34
|
+
* Simple 32-bit seeded PRNG (Mulberry32).
|
|
35
|
+
* Returns a function that produces floats in [0, 1).
|
|
36
|
+
*/
|
|
37
|
+
function createSeededRng(seed) {
|
|
38
|
+
let s = seed | 0;
|
|
39
|
+
return () => {
|
|
40
|
+
s = (s + 0x6d2b79f5) | 0;
|
|
41
|
+
let t = Math.imul(s ^ (s >>> 15), 1 | s);
|
|
42
|
+
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
|
|
43
|
+
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
// ============================================================================
|
|
47
|
+
// SpectralSparsifier
|
|
48
|
+
// ============================================================================
|
|
49
|
+
/**
|
|
50
|
+
* Oversampling constant for the edge budget.
|
|
51
|
+
*
|
|
52
|
+
* Controls the base edge budget as a multiple of (n-1). A value of 2
|
|
53
|
+
* targets roughly 2*(n-1) edges for epsilon near 1 (weak approximation),
|
|
54
|
+
* scaling up as epsilon decreases (stronger approximation keeps more).
|
|
55
|
+
*/
|
|
56
|
+
const BUDGET_BASE_MULTIPLIER = 2.0;
|
|
57
|
+
/** Default number of eigenvalues to compare during validation */
|
|
58
|
+
const DEFAULT_VALIDATION_K = 10;
|
|
59
|
+
/** Maximum power-iteration steps for eigenvalue computation */
|
|
60
|
+
const MAX_POWER_ITER = 200;
|
|
61
|
+
/** Convergence tolerance for power iteration */
|
|
62
|
+
const POWER_TOL = 1e-8;
|
|
63
|
+
/**
|
|
64
|
+
* Spectral graph sparsifier using degree-based leverage score sampling.
|
|
65
|
+
*
|
|
66
|
+
* Compresses a weighted undirected graph while approximately preserving the
|
|
67
|
+
* spectrum of its Laplacian. Uses a degree-based heuristic for leverage
|
|
68
|
+
* scores (not true effective resistance). The approximation quality is
|
|
69
|
+
* controlled by `epsilon`: eigenvalues of the sparsified Laplacian should
|
|
70
|
+
* lie within a (1 +/- epsilon) factor of the original.
|
|
71
|
+
*/
|
|
72
|
+
export class SpectralSparsifier {
|
|
73
|
+
epsilon;
|
|
74
|
+
seed;
|
|
75
|
+
constructor(config) {
|
|
76
|
+
this.epsilon = config?.epsilon ?? 0.3;
|
|
77
|
+
this.seed = config?.seed;
|
|
78
|
+
if (this.epsilon <= 0 || this.epsilon >= 1) {
|
|
79
|
+
throw new Error('SparsifierConfig.epsilon must be in (0, 1)');
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// --------------------------------------------------------------------------
|
|
83
|
+
// Public API
|
|
84
|
+
// --------------------------------------------------------------------------
|
|
85
|
+
/**
|
|
86
|
+
* Sparsify a graph while preserving spectral properties.
|
|
87
|
+
*
|
|
88
|
+
* @param graph - The input weighted undirected graph
|
|
89
|
+
* @returns A new graph with fewer edges whose Laplacian spectrum
|
|
90
|
+
* approximates the original within (1 +/- epsilon).
|
|
91
|
+
*/
|
|
92
|
+
sparsify(graph) {
|
|
93
|
+
if (graph.nodeCount <= 0 || graph.edges.length === 0) {
|
|
94
|
+
return { nodeCount: graph.nodeCount, edges: [] };
|
|
95
|
+
}
|
|
96
|
+
const rng = this.seed !== undefined ? createSeededRng(this.seed) : Math.random;
|
|
97
|
+
// Step 1: compute approximate effective resistances (leverage scores)
|
|
98
|
+
const resistances = this.approximateEffectiveResistances(graph);
|
|
99
|
+
// Step 2: compute raw leverage scores and target edge budget
|
|
100
|
+
const n = graph.nodeCount;
|
|
101
|
+
const m = graph.edges.length;
|
|
102
|
+
const logN = Math.max(1, Math.log(n));
|
|
103
|
+
const eps2 = this.epsilon * this.epsilon;
|
|
104
|
+
// Target = B * (n-1) * (1-eps), clamped to [n-1, m].
|
|
105
|
+
// Higher epsilon => fewer edges; at eps=0.5, target = spanning tree size.
|
|
106
|
+
// For sparse graphs near tree threshold, floor of (n-1) preserves connectivity.
|
|
107
|
+
const targetEdges = Math.min(m, Math.max(n - 1, BUDGET_BASE_MULTIPLIER * (n - 1) * (1 - this.epsilon)));
|
|
108
|
+
// Raw leverage scores: w_e * R_eff(u,v)
|
|
109
|
+
const rawScores = new Float64Array(m);
|
|
110
|
+
let totalScore = 0;
|
|
111
|
+
for (let i = 0; i < m; i++) {
|
|
112
|
+
const w = graph.edges[i][2];
|
|
113
|
+
rawScores[i] = w * resistances[i];
|
|
114
|
+
totalScore += rawScores[i];
|
|
115
|
+
}
|
|
116
|
+
// If total leverage score is zero (degenerate), return empty
|
|
117
|
+
if (totalScore <= 0) {
|
|
118
|
+
return { nodeCount: graph.nodeCount, edges: [] };
|
|
119
|
+
}
|
|
120
|
+
// Step 3: sample edges proportional to leverage scores, scaled to targetEdges
|
|
121
|
+
const sampledEdges = [];
|
|
122
|
+
for (let i = 0; i < m; i++) {
|
|
123
|
+
const [u, v, w] = graph.edges[i];
|
|
124
|
+
const p = Math.min(1, (rawScores[i] / totalScore) * targetEdges);
|
|
125
|
+
if (rng() < p) {
|
|
126
|
+
// Rescale weight to maintain unbiased expectation
|
|
127
|
+
const rescaledWeight = w / p;
|
|
128
|
+
sampledEdges.push([u, v, rescaledWeight]);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return { nodeCount: graph.nodeCount, edges: sampledEdges };
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Validate that a sparsified graph preserves the spectral properties of
|
|
135
|
+
* the original. Compares the top-k non-trivial Laplacian eigenvalues.
|
|
136
|
+
*
|
|
137
|
+
* @param original - The original graph
|
|
138
|
+
* @param sparsified - The sparsified graph
|
|
139
|
+
* @returns Validation result including eigenvalue ratios and compression info
|
|
140
|
+
*/
|
|
141
|
+
validateSpectral(original, sparsified) {
|
|
142
|
+
const k = Math.min(DEFAULT_VALIDATION_K, Math.max(1, original.nodeCount - 1));
|
|
143
|
+
const origLaplacian = this.computeLaplacian(original);
|
|
144
|
+
const sparsLaplacian = this.computeLaplacian(sparsified);
|
|
145
|
+
const origEigenvalues = this.computeTopEigenvalues(origLaplacian, k);
|
|
146
|
+
const sparsEigenvalues = this.computeTopEigenvalues(sparsLaplacian, k);
|
|
147
|
+
// Compute ratios, skipping near-zero original eigenvalues
|
|
148
|
+
const ratios = [];
|
|
149
|
+
let allValid = true;
|
|
150
|
+
const eps = this.epsilon;
|
|
151
|
+
for (let i = 0; i < origEigenvalues.length; i++) {
|
|
152
|
+
if (origEigenvalues[i] < 1e-10) {
|
|
153
|
+
// Skip trivial / near-zero eigenvalues
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
const ratio = sparsEigenvalues[i] / origEigenvalues[i];
|
|
157
|
+
ratios.push(ratio);
|
|
158
|
+
if (ratio < 1 - eps || ratio > 1 + eps) {
|
|
159
|
+
allValid = false;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return {
|
|
163
|
+
isValid: allValid,
|
|
164
|
+
eigenvalueRatios: ratios,
|
|
165
|
+
originalEdgeCount: original.edges.length,
|
|
166
|
+
sparsifiedEdgeCount: sparsified.edges.length,
|
|
167
|
+
compressionRatio: original.edges.length > 0
|
|
168
|
+
? sparsified.edges.length / original.edges.length
|
|
169
|
+
: 0,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Compute the dense graph Laplacian matrix L = D - W.
|
|
174
|
+
*
|
|
175
|
+
* For weighted undirected graphs, L[i][i] = sum of weights of edges
|
|
176
|
+
* incident to i, and L[i][j] = -w(i,j).
|
|
177
|
+
*
|
|
178
|
+
* @param graph - Input graph
|
|
179
|
+
* @returns n x n Laplacian matrix as number[][]
|
|
180
|
+
*/
|
|
181
|
+
computeLaplacian(graph) {
|
|
182
|
+
const n = graph.nodeCount;
|
|
183
|
+
const L = Array.from({ length: n }, () => new Array(n).fill(0));
|
|
184
|
+
for (const [u, v, w] of graph.edges) {
|
|
185
|
+
if (u < 0 || u >= n || v < 0 || v >= n)
|
|
186
|
+
continue;
|
|
187
|
+
L[u][u] += w;
|
|
188
|
+
L[v][v] += w;
|
|
189
|
+
L[u][v] -= w;
|
|
190
|
+
L[v][u] -= w;
|
|
191
|
+
}
|
|
192
|
+
return L;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Compute the top-k eigenvalues of a symmetric matrix using power
|
|
196
|
+
* iteration with deflation.
|
|
197
|
+
*
|
|
198
|
+
* Eigenvalues are returned in descending order.
|
|
199
|
+
*
|
|
200
|
+
* @param matrix - Symmetric n x n matrix
|
|
201
|
+
* @param k - Number of eigenvalues to compute
|
|
202
|
+
* @returns Array of up to k eigenvalues in descending order
|
|
203
|
+
*/
|
|
204
|
+
computeTopEigenvalues(matrix, k) {
|
|
205
|
+
const n = matrix.length;
|
|
206
|
+
if (n === 0)
|
|
207
|
+
return [];
|
|
208
|
+
const eigenvalues = [];
|
|
209
|
+
const eigenvectors = [];
|
|
210
|
+
// Work on a copy so deflation doesn't mutate the input
|
|
211
|
+
const M = matrix.map(row => [...row]);
|
|
212
|
+
for (let iter = 0; iter < Math.min(k, n); iter++) {
|
|
213
|
+
const result = this.powerIteration(M, n, eigenvectors);
|
|
214
|
+
if (result === null)
|
|
215
|
+
break;
|
|
216
|
+
eigenvalues.push(result.eigenvalue);
|
|
217
|
+
eigenvectors.push(result.eigenvector);
|
|
218
|
+
// Deflate: M = M - lambda * v * v^T
|
|
219
|
+
deflateMatrix(M, result.eigenvalue, result.eigenvector);
|
|
220
|
+
}
|
|
221
|
+
return eigenvalues;
|
|
222
|
+
}
|
|
223
|
+
// --------------------------------------------------------------------------
|
|
224
|
+
// Private helpers
|
|
225
|
+
// --------------------------------------------------------------------------
|
|
226
|
+
/**
|
|
227
|
+
* Approximate effective resistance for every edge using a leverage-score
|
|
228
|
+
* heuristic based on node degrees.
|
|
229
|
+
*
|
|
230
|
+
* For edge (u, v) with weight w:
|
|
231
|
+
* R_eff(u, v) ~= 1/deg_w(u) + 1/deg_w(v)
|
|
232
|
+
*
|
|
233
|
+
* where deg_w(u) is the weighted degree of u.
|
|
234
|
+
* This is a rough but computationally cheap O(m) approximation that
|
|
235
|
+
* captures the intuition that edges between low-degree nodes have higher
|
|
236
|
+
* effective resistance.
|
|
237
|
+
*/
|
|
238
|
+
approximateEffectiveResistances(graph) {
|
|
239
|
+
const n = graph.nodeCount;
|
|
240
|
+
const weightedDegree = new Float64Array(n);
|
|
241
|
+
// Compute weighted degrees
|
|
242
|
+
for (const [u, v, w] of graph.edges) {
|
|
243
|
+
if (u >= 0 && u < n)
|
|
244
|
+
weightedDegree[u] += w;
|
|
245
|
+
if (v >= 0 && v < n)
|
|
246
|
+
weightedDegree[v] += w;
|
|
247
|
+
}
|
|
248
|
+
// Approximate effective resistance per edge
|
|
249
|
+
const resistances = new Array(graph.edges.length);
|
|
250
|
+
for (let i = 0; i < graph.edges.length; i++) {
|
|
251
|
+
const [u, v] = graph.edges[i];
|
|
252
|
+
const degU = weightedDegree[u] || 1;
|
|
253
|
+
const degV = weightedDegree[v] || 1;
|
|
254
|
+
resistances[i] = 1 / degU + 1 / degV;
|
|
255
|
+
}
|
|
256
|
+
return resistances;
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Single round of power iteration to find the dominant eigenvalue/vector,
|
|
260
|
+
* deflating away previously found eigenvectors.
|
|
261
|
+
*
|
|
262
|
+
* Uses a deterministic but well-spread initial vector and handles
|
|
263
|
+
* near-zero eigenvalues by checking the matrix's remaining Frobenius norm.
|
|
264
|
+
*/
|
|
265
|
+
powerIteration(M, n, previousEigenvectors) {
|
|
266
|
+
// Check if the matrix still has significant entries (Frobenius norm)
|
|
267
|
+
let frobSq = 0;
|
|
268
|
+
for (let i = 0; i < n; i++) {
|
|
269
|
+
for (let j = 0; j < n; j++) {
|
|
270
|
+
frobSq += M[i][j] * M[i][j];
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
if (frobSq < POWER_TOL * POWER_TOL)
|
|
274
|
+
return null;
|
|
275
|
+
// Deterministic but well-spread initial vector
|
|
276
|
+
// Use a different pattern for each deflation round to avoid
|
|
277
|
+
// starting in the null-space of the deflated matrix
|
|
278
|
+
const round = previousEigenvectors.length;
|
|
279
|
+
let v = new Array(n);
|
|
280
|
+
for (let i = 0; i < n; i++) {
|
|
281
|
+
v[i] = Math.sin((i + 1) * (round + 1) * 1.618033988749895) + 0.1 * (i - n / 2);
|
|
282
|
+
}
|
|
283
|
+
// Deflate against previously found eigenvectors
|
|
284
|
+
for (const prev of previousEigenvectors) {
|
|
285
|
+
deflateVector(v, prev);
|
|
286
|
+
}
|
|
287
|
+
const initNorm = normalizeVector(v);
|
|
288
|
+
if (initNorm < POWER_TOL)
|
|
289
|
+
return null;
|
|
290
|
+
let eigenvalue = 0;
|
|
291
|
+
for (let iter = 0; iter < MAX_POWER_ITER; iter++) {
|
|
292
|
+
// w = M * v
|
|
293
|
+
const w = matVecMul(M, v);
|
|
294
|
+
// Deflate against previous eigenvectors
|
|
295
|
+
for (const prev of previousEigenvectors) {
|
|
296
|
+
deflateVector(w, prev);
|
|
297
|
+
}
|
|
298
|
+
const newEigen = dotProduct(w, v);
|
|
299
|
+
const norm = vectorNorm(w);
|
|
300
|
+
if (norm < POWER_TOL) {
|
|
301
|
+
// The vector collapsed: eigenvalue is effectively 0
|
|
302
|
+
return { eigenvalue: 0, eigenvector: v };
|
|
303
|
+
}
|
|
304
|
+
// Normalize
|
|
305
|
+
for (let i = 0; i < n; i++)
|
|
306
|
+
v[i] = w[i] / norm;
|
|
307
|
+
if (Math.abs(newEigen - eigenvalue) < POWER_TOL) {
|
|
308
|
+
eigenvalue = newEigen;
|
|
309
|
+
break;
|
|
310
|
+
}
|
|
311
|
+
eigenvalue = newEigen;
|
|
312
|
+
}
|
|
313
|
+
return { eigenvalue: Math.abs(eigenvalue), eigenvector: v };
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
// ============================================================================
|
|
317
|
+
// Linear Algebra Helpers (module-private)
|
|
318
|
+
// ============================================================================
|
|
319
|
+
/** Matrix-vector multiplication: result = M * v */
|
|
320
|
+
function matVecMul(M, v) {
|
|
321
|
+
const n = M.length;
|
|
322
|
+
const result = new Array(n).fill(0);
|
|
323
|
+
for (let i = 0; i < n; i++) {
|
|
324
|
+
let sum = 0;
|
|
325
|
+
for (let j = 0; j < n; j++) {
|
|
326
|
+
sum += M[i][j] * v[j];
|
|
327
|
+
}
|
|
328
|
+
result[i] = sum;
|
|
329
|
+
}
|
|
330
|
+
return result;
|
|
331
|
+
}
|
|
332
|
+
/** L2 norm of a vector */
|
|
333
|
+
function vectorNorm(v) {
|
|
334
|
+
let sum = 0;
|
|
335
|
+
for (let i = 0; i < v.length; i++)
|
|
336
|
+
sum += v[i] * v[i];
|
|
337
|
+
return Math.sqrt(sum);
|
|
338
|
+
}
|
|
339
|
+
/** Normalize a vector in-place. Returns the norm before normalization. */
|
|
340
|
+
function normalizeVector(v) {
|
|
341
|
+
const norm = vectorNorm(v);
|
|
342
|
+
if (norm > 0) {
|
|
343
|
+
for (let i = 0; i < v.length; i++)
|
|
344
|
+
v[i] /= norm;
|
|
345
|
+
}
|
|
346
|
+
return norm;
|
|
347
|
+
}
|
|
348
|
+
/** Dot product of two vectors */
|
|
349
|
+
function dotProduct(a, b) {
|
|
350
|
+
let sum = 0;
|
|
351
|
+
for (let i = 0; i < a.length; i++)
|
|
352
|
+
sum += a[i] * b[i];
|
|
353
|
+
return sum;
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Remove the component of v along the direction of u (projection).
|
|
357
|
+
* v = v - (v . u) * u
|
|
358
|
+
* Assumes u is normalised.
|
|
359
|
+
*/
|
|
360
|
+
function deflateVector(v, u) {
|
|
361
|
+
const dot = dotProduct(v, u);
|
|
362
|
+
for (let i = 0; i < v.length; i++)
|
|
363
|
+
v[i] -= dot * u[i];
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Deflate a matrix: M = M - lambda * v * v^T
|
|
367
|
+
* Used after extracting an eigenvalue to remove its contribution.
|
|
368
|
+
*/
|
|
369
|
+
function deflateMatrix(M, lambda, v) {
|
|
370
|
+
const n = v.length;
|
|
371
|
+
for (let i = 0; i < n; i++) {
|
|
372
|
+
for (let j = 0; j < n; j++) {
|
|
373
|
+
M[i][j] -= lambda * v[i] * v[j];
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
// ============================================================================
|
|
378
|
+
// Factory Function
|
|
379
|
+
// ============================================================================
|
|
380
|
+
/**
|
|
381
|
+
* Create a spectral sparsifier with the given configuration.
|
|
382
|
+
*
|
|
383
|
+
* @param config - Optional partial configuration
|
|
384
|
+
* @returns A new SpectralSparsifier instance
|
|
385
|
+
*/
|
|
386
|
+
export function createSpectralSparsifier(config) {
|
|
387
|
+
return new SpectralSparsifier(config);
|
|
388
|
+
}
|
|
389
|
+
//# sourceMappingURL=spectral-sparsifier.js.map
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Granger Causality for Test Failure Prediction (R12, ADR-087 Milestone 4)
|
|
3
|
+
*
|
|
4
|
+
* Temporal causal discovery: finds causal chains between test failures.
|
|
5
|
+
* E.g. "when test_login fails, test_checkout fails 5min later at 87%."
|
|
6
|
+
*
|
|
7
|
+
* Algorithm: Bivariate VAR(p) + F-test with OLS via Gaussian elimination
|
|
8
|
+
* and regularized incomplete beta function for F-distribution CDF.
|
|
9
|
+
*
|
|
10
|
+
* @module integrations/ruvector/temporal-causality
|
|
11
|
+
*/
|
|
12
|
+
/** A time series of test execution outcomes */
|
|
13
|
+
export interface TestExecutionHistory {
|
|
14
|
+
testId: string;
|
|
15
|
+
/** Sorted ascending */
|
|
16
|
+
timestamps: number[];
|
|
17
|
+
/** 1 = pass, 0 = fail */
|
|
18
|
+
outcomes: number[];
|
|
19
|
+
}
|
|
20
|
+
/** A discovered causal link between two tests */
|
|
21
|
+
export interface CausalLink {
|
|
22
|
+
sourceTestId: string;
|
|
23
|
+
targetTestId: string;
|
|
24
|
+
lag: number;
|
|
25
|
+
fStatistic: number;
|
|
26
|
+
pValue: number;
|
|
27
|
+
/** Average absolute coefficient magnitude */
|
|
28
|
+
strength: number;
|
|
29
|
+
direction: 'positive' | 'negative';
|
|
30
|
+
}
|
|
31
|
+
/** Configuration for the Granger analyzer */
|
|
32
|
+
export interface GrangerConfig {
|
|
33
|
+
maxLag: number;
|
|
34
|
+
alpha: number;
|
|
35
|
+
minSeriesLength: number;
|
|
36
|
+
}
|
|
37
|
+
/** Lanczos approximation of ln(Gamma(z)). */
|
|
38
|
+
declare function lnGamma(z: number): number;
|
|
39
|
+
/** Regularized incomplete beta I_x(a,b) via continued fraction (Lentz). */
|
|
40
|
+
declare function regularizedIncompleteBeta(x: number, a: number, b: number): number;
|
|
41
|
+
/** F-distribution CDF via incomplete beta: P(F<=x) = I_{d1*x/(d1*x+d2)}(d1/2, d2/2) */
|
|
42
|
+
declare function fDistributionCDF(x: number, d1: number, d2: number): number;
|
|
43
|
+
/** OLS: beta = (X'X)^{-1} X'y via normal equations. */
|
|
44
|
+
declare function olsRegression(X: number[][], y: number[]): number[] | null;
|
|
45
|
+
/** Align two series to a common time grid (nearest-neighbor binning). */
|
|
46
|
+
declare function alignTimeSeries(source: TestExecutionHistory, target: TestExecutionHistory): {
|
|
47
|
+
sourceOutcomes: number[];
|
|
48
|
+
targetOutcomes: number[];
|
|
49
|
+
} | null;
|
|
50
|
+
export declare class GrangerAnalyzer {
|
|
51
|
+
private readonly config;
|
|
52
|
+
constructor(config?: Partial<GrangerConfig>);
|
|
53
|
+
/** Analyze all pairs; returns only significant links (p < alpha). */
|
|
54
|
+
analyzeCausality(timeSeries: TestExecutionHistory[]): CausalLink[];
|
|
55
|
+
/** Test Granger causality for one pair at one lag via VAR(p) + F-test. */
|
|
56
|
+
testPairwise(source: TestExecutionHistory, target: TestExecutionHistory, lag: number): CausalLink;
|
|
57
|
+
/** True if the link is statistically significant at the configured alpha. */
|
|
58
|
+
significanceTest(link: CausalLink): boolean;
|
|
59
|
+
private arraysEqual;
|
|
60
|
+
}
|
|
61
|
+
export declare function createGrangerAnalyzer(config?: Partial<GrangerConfig>): GrangerAnalyzer;
|
|
62
|
+
export { regularizedIncompleteBeta as _regularizedIncompleteBeta, fDistributionCDF as _fDistributionCDF, lnGamma as _lnGamma, olsRegression as _olsRegression, alignTimeSeries as _alignTimeSeries, };
|
|
63
|
+
//# sourceMappingURL=temporal-causality.d.ts.map
|