agentic-qe 3.7.5 → 3.7.6
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/skills-manifest.json +1 -1
- package/CHANGELOG.md +18 -0
- package/dist/cli/bundle.js +5199 -1335
- package/dist/cli/commands/security.d.ts.map +1 -1
- package/dist/cli/commands/security.js +66 -1
- package/dist/cli/commands/security.js.map +1 -1
- package/dist/cli/commands/test.d.ts.map +1 -1
- package/dist/cli/commands/test.js +86 -3
- package/dist/cli/commands/test.js.map +1 -1
- package/dist/cli/index.js +119 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/coordination/workflow-orchestrator.d.ts.map +1 -1
- package/dist/coordination/workflow-orchestrator.js +2 -6
- package/dist/coordination/workflow-orchestrator.js.map +1 -1
- package/dist/mcp/bundle.js +3977 -153
- package/dist/mcp/handlers/core-handlers.d.ts.map +1 -1
- package/dist/mcp/handlers/core-handlers.js +35 -0
- package/dist/mcp/handlers/core-handlers.js.map +1 -1
- package/dist/mcp/protocol-server.d.ts.map +1 -1
- package/dist/mcp/protocol-server.js +4 -1
- package/dist/mcp/protocol-server.js.map +1 -1
- package/dist/mcp/qe-tool-bridge.d.ts +27 -0
- package/dist/mcp/qe-tool-bridge.d.ts.map +1 -0
- package/dist/mcp/qe-tool-bridge.js +87 -0
- package/dist/mcp/qe-tool-bridge.js.map +1 -0
- package/dist/mcp/tools/registry.d.ts +4 -0
- package/dist/mcp/tools/registry.d.ts.map +1 -1
- package/dist/mcp/tools/registry.js +20 -0
- package/dist/mcp/tools/registry.js.map +1 -1
- package/dist/mcp/tools/security-compliance/visual-security.d.ts +45 -0
- package/dist/mcp/tools/security-compliance/visual-security.d.ts.map +1 -0
- package/dist/mcp/tools/security-compliance/visual-security.js +218 -0
- package/dist/mcp/tools/security-compliance/visual-security.js.map +1 -0
- package/dist/mcp/tools/test-execution/browser-workflow.d.ts +50 -0
- package/dist/mcp/tools/test-execution/browser-workflow.d.ts.map +1 -0
- package/dist/mcp/tools/test-execution/browser-workflow.js +145 -0
- package/dist/mcp/tools/test-execution/browser-workflow.js.map +1 -0
- package/dist/mcp/tools/test-execution/load-test.d.ts +37 -0
- package/dist/mcp/tools/test-execution/load-test.d.ts.map +1 -0
- package/dist/mcp/tools/test-execution/load-test.js +98 -0
- package/dist/mcp/tools/test-execution/load-test.js.map +1 -0
- package/dist/mcp/tools/test-execution/schedule.d.ts +44 -0
- package/dist/mcp/tools/test-execution/schedule.d.ts.map +1 -0
- package/dist/mcp/tools/test-execution/schedule.js +96 -0
- package/dist/mcp/tools/test-execution/schedule.js.map +1 -0
- package/dist/planning/goap-planner.d.ts.map +1 -1
- package/dist/planning/goap-planner.js +7 -28
- package/dist/planning/goap-planner.js.map +1 -1
- package/dist/planning/plan-executor.d.ts.map +1 -1
- package/dist/planning/plan-executor.js +7 -28
- package/dist/planning/plan-executor.js.map +1 -1
- package/package.json +3 -10
- package/dist/cli/commands/qe-tools.d.ts +0 -27
- package/dist/cli/commands/qe-tools.d.ts.map +0 -1
- package/dist/cli/commands/qe-tools.js +0 -771
- package/dist/cli/commands/qe-tools.js.map +0 -1
- package/dist/neural-optimizer/index.d.ts +0 -55
- package/dist/neural-optimizer/index.d.ts.map +0 -1
- package/dist/neural-optimizer/index.js +0 -57
- package/dist/neural-optimizer/index.js.map +0 -1
- package/dist/neural-optimizer/replay-buffer.d.ts +0 -126
- package/dist/neural-optimizer/replay-buffer.d.ts.map +0 -1
- package/dist/neural-optimizer/replay-buffer.js +0 -356
- package/dist/neural-optimizer/replay-buffer.js.map +0 -1
- package/dist/neural-optimizer/swarm-topology.d.ts +0 -157
- package/dist/neural-optimizer/swarm-topology.d.ts.map +0 -1
- package/dist/neural-optimizer/swarm-topology.js +0 -384
- package/dist/neural-optimizer/swarm-topology.js.map +0 -1
- package/dist/neural-optimizer/topology-optimizer.d.ts +0 -137
- package/dist/neural-optimizer/topology-optimizer.d.ts.map +0 -1
- package/dist/neural-optimizer/topology-optimizer.js +0 -657
- package/dist/neural-optimizer/topology-optimizer.js.map +0 -1
- package/dist/neural-optimizer/types.d.ts +0 -333
- package/dist/neural-optimizer/types.d.ts.map +0 -1
- package/dist/neural-optimizer/types.js +0 -57
- package/dist/neural-optimizer/types.js.map +0 -1
- package/dist/neural-optimizer/value-network.d.ts +0 -129
- package/dist/neural-optimizer/value-network.d.ts.map +0 -1
- package/dist/neural-optimizer/value-network.js +0 -279
- package/dist/neural-optimizer/value-network.js.map +0 -1
|
@@ -1,657 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Agentic QE v3 - Neural Topology Optimizer
|
|
3
|
-
* ADR-034: RL-based swarm topology optimization
|
|
4
|
-
*
|
|
5
|
-
* Implements reinforcement learning-based topology optimization:
|
|
6
|
-
* - Q-learning with value network for state evaluation
|
|
7
|
-
* - Experience replay for stable training
|
|
8
|
-
* - Epsilon-greedy exploration
|
|
9
|
-
* - Multi-objective reward (min-cut, efficiency, load balance)
|
|
10
|
-
*/
|
|
11
|
-
import { DEFAULT_OPTIMIZER_CONFIG, actionToIndex, indexToActionType, ACTION_TYPES } from './types';
|
|
12
|
-
import { ValueNetwork } from './value-network';
|
|
13
|
-
import { PrioritizedReplayBuffer } from './replay-buffer';
|
|
14
|
-
import { secureRandom, secureRandomInt } from '../shared/utils/crypto-random.js';
|
|
15
|
-
// ============================================================================
|
|
16
|
-
// Neural Topology Optimizer
|
|
17
|
-
// ============================================================================
|
|
18
|
-
/**
|
|
19
|
-
* Neural Topology Optimizer using reinforcement learning
|
|
20
|
-
*
|
|
21
|
-
* Uses a value network to estimate state values and learns to select
|
|
22
|
-
* topology modifications that improve overall swarm performance.
|
|
23
|
-
*/
|
|
24
|
-
export class NeuralTopologyOptimizer {
|
|
25
|
-
/** Primary value network */
|
|
26
|
-
valueNetwork;
|
|
27
|
-
/** Target network for stable learning */
|
|
28
|
-
targetNetwork;
|
|
29
|
-
/** Experience replay buffer */
|
|
30
|
-
replayBuffer;
|
|
31
|
-
/** Optimizer configuration */
|
|
32
|
-
config;
|
|
33
|
-
/** Reference to topology being optimized */
|
|
34
|
-
topology;
|
|
35
|
-
/** Previous state for learning */
|
|
36
|
-
prevState = null;
|
|
37
|
-
/** Previous min-cut estimate */
|
|
38
|
-
prevMinCut = 0;
|
|
39
|
-
/** Current exploration rate */
|
|
40
|
-
epsilon;
|
|
41
|
-
/** Simulation time */
|
|
42
|
-
time = 0;
|
|
43
|
-
/** Total optimization steps */
|
|
44
|
-
totalSteps = 0;
|
|
45
|
-
/** Episode count */
|
|
46
|
-
episodes = 0;
|
|
47
|
-
/** Cumulative reward */
|
|
48
|
-
cumulativeReward = 0;
|
|
49
|
-
/** Action counts */
|
|
50
|
-
actionCounts;
|
|
51
|
-
/** Min-cut history */
|
|
52
|
-
minCutHistory = [];
|
|
53
|
-
/** Reward history */
|
|
54
|
-
rewardHistory = [];
|
|
55
|
-
/** Last action taken (for feedback) */
|
|
56
|
-
lastAction = null;
|
|
57
|
-
constructor(topology, config = {}) {
|
|
58
|
-
this.topology = topology;
|
|
59
|
-
this.config = { ...DEFAULT_OPTIMIZER_CONFIG, ...config };
|
|
60
|
-
this.epsilon = this.config.epsilon;
|
|
61
|
-
// Initialize value networks
|
|
62
|
-
this.valueNetwork = new ValueNetwork(this.config.inputSize, this.config.hiddenSize);
|
|
63
|
-
this.targetNetwork = new ValueNetwork(this.config.inputSize, this.config.hiddenSize);
|
|
64
|
-
this.targetNetwork.copyFrom(this.valueNetwork);
|
|
65
|
-
// Initialize replay buffer
|
|
66
|
-
this.replayBuffer = new PrioritizedReplayBuffer(this.config.replayBufferSize, { alpha: 0.6, beta: 0.4 });
|
|
67
|
-
// Initialize action counts
|
|
68
|
-
this.actionCounts = {};
|
|
69
|
-
for (const actionType of ACTION_TYPES) {
|
|
70
|
-
this.actionCounts[actionType] = 0;
|
|
71
|
-
}
|
|
72
|
-
// Initialize state
|
|
73
|
-
this.prevState = this.extractFeatures();
|
|
74
|
-
this.prevMinCut = this.estimateMinCut();
|
|
75
|
-
}
|
|
76
|
-
// ============================================================================
|
|
77
|
-
// Public API
|
|
78
|
-
// ============================================================================
|
|
79
|
-
/**
|
|
80
|
-
* Run one optimization step
|
|
81
|
-
*/
|
|
82
|
-
optimizeStep() {
|
|
83
|
-
// 1. Encode current state
|
|
84
|
-
const state = this.extractFeatures();
|
|
85
|
-
const valueBefore = this.valueNetwork.estimate(state);
|
|
86
|
-
// 2. Select action using epsilon-greedy
|
|
87
|
-
const action = this.selectAction(state);
|
|
88
|
-
this.lastAction = action;
|
|
89
|
-
// 3. Execute action
|
|
90
|
-
const oldMinCut = this.estimateMinCut();
|
|
91
|
-
this.applyAction(action);
|
|
92
|
-
const newMinCut = this.estimateMinCut();
|
|
93
|
-
// 4. Calculate multi-objective reward
|
|
94
|
-
const reward = this.calculateReward(oldMinCut, newMinCut);
|
|
95
|
-
// 5. Get new state
|
|
96
|
-
const nextState = this.extractFeatures();
|
|
97
|
-
const valueAfter = this.valueNetwork.estimate(nextState);
|
|
98
|
-
// 6. Calculate TD error
|
|
99
|
-
const targetValue = reward + this.config.gamma * this.targetNetwork.estimate(nextState);
|
|
100
|
-
const tdError = targetValue - valueBefore;
|
|
101
|
-
// 7. Update value network
|
|
102
|
-
this.valueNetwork.update(state, tdError, this.config.learningRate);
|
|
103
|
-
// 8. Store experience
|
|
104
|
-
if (this.prevState !== null) {
|
|
105
|
-
const experience = {
|
|
106
|
-
state: this.prevState,
|
|
107
|
-
actionIdx: actionToIndex(action),
|
|
108
|
-
reward,
|
|
109
|
-
nextState,
|
|
110
|
-
done: false,
|
|
111
|
-
tdError: Math.abs(tdError),
|
|
112
|
-
timestamp: Date.now(),
|
|
113
|
-
};
|
|
114
|
-
this.replayBuffer.push(experience);
|
|
115
|
-
}
|
|
116
|
-
// 9. Train from replay buffer
|
|
117
|
-
if (this.replayBuffer.length >= this.config.minExperiencesForTraining) {
|
|
118
|
-
this.trainFromReplay();
|
|
119
|
-
}
|
|
120
|
-
// 10. Update target network periodically
|
|
121
|
-
this.totalSteps++;
|
|
122
|
-
if (this.totalSteps % this.config.targetUpdateFrequency === 0) {
|
|
123
|
-
this.targetNetwork.softUpdate(this.valueNetwork, 0.01);
|
|
124
|
-
}
|
|
125
|
-
// 11. Decay exploration rate
|
|
126
|
-
this.epsilon = Math.max(this.config.minEpsilon, this.epsilon * this.config.epsilonDecay);
|
|
127
|
-
// 12. Update tracking
|
|
128
|
-
this.prevState = nextState;
|
|
129
|
-
this.prevMinCut = newMinCut;
|
|
130
|
-
this.time += this.config.dt;
|
|
131
|
-
this.actionCounts[action.type]++;
|
|
132
|
-
this.cumulativeReward += reward;
|
|
133
|
-
this.minCutHistory.push(newMinCut);
|
|
134
|
-
this.rewardHistory.push(reward);
|
|
135
|
-
// Limit history size
|
|
136
|
-
if (this.minCutHistory.length > 1000) {
|
|
137
|
-
this.minCutHistory.shift();
|
|
138
|
-
this.rewardHistory.shift();
|
|
139
|
-
}
|
|
140
|
-
// Calculate metrics
|
|
141
|
-
const loadStats = this.getLoadStats();
|
|
142
|
-
return {
|
|
143
|
-
action,
|
|
144
|
-
reward,
|
|
145
|
-
newMinCut,
|
|
146
|
-
communicationEfficiency: this.measureCommunicationEfficiency(),
|
|
147
|
-
loadBalance: 1 - loadStats.variance,
|
|
148
|
-
tdError,
|
|
149
|
-
epsilon: this.epsilon,
|
|
150
|
-
valueBefore,
|
|
151
|
-
valueAfter,
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
|
-
/**
|
|
155
|
-
* Run multiple optimization steps
|
|
156
|
-
*/
|
|
157
|
-
optimize(steps) {
|
|
158
|
-
const results = [];
|
|
159
|
-
for (let i = 0; i < steps; i++) {
|
|
160
|
-
results.push(this.optimizeStep());
|
|
161
|
-
}
|
|
162
|
-
this.episodes++;
|
|
163
|
-
return results;
|
|
164
|
-
}
|
|
165
|
-
/**
|
|
166
|
-
* Provide external feedback (e.g., from task completion)
|
|
167
|
-
*/
|
|
168
|
-
provideFeedback(reward) {
|
|
169
|
-
if (this.prevState === null || this.lastAction === null)
|
|
170
|
-
return;
|
|
171
|
-
const state = this.extractFeatures();
|
|
172
|
-
const experience = {
|
|
173
|
-
state: this.prevState,
|
|
174
|
-
actionIdx: actionToIndex(this.lastAction),
|
|
175
|
-
reward,
|
|
176
|
-
nextState: state,
|
|
177
|
-
done: false,
|
|
178
|
-
tdError: Math.abs(reward),
|
|
179
|
-
timestamp: Date.now(),
|
|
180
|
-
};
|
|
181
|
-
this.replayBuffer.push(experience);
|
|
182
|
-
// Immediate learning from feedback
|
|
183
|
-
const currentValue = this.valueNetwork.estimate(this.prevState);
|
|
184
|
-
const nextValue = this.targetNetwork.estimate(state);
|
|
185
|
-
const tdError = reward + this.config.gamma * nextValue - currentValue;
|
|
186
|
-
this.valueNetwork.update(this.prevState, tdError, this.config.learningRate);
|
|
187
|
-
}
|
|
188
|
-
/**
|
|
189
|
-
* Get skip regions (low activity areas)
|
|
190
|
-
*/
|
|
191
|
-
getSkipRegions() {
|
|
192
|
-
return this.topology.agents
|
|
193
|
-
.filter((agent) => {
|
|
194
|
-
const degree = this.topology.connections.filter((c) => c.from === agent.id || c.to === agent.id).length;
|
|
195
|
-
return degree < 2;
|
|
196
|
-
})
|
|
197
|
-
.map((agent) => agent.id);
|
|
198
|
-
}
|
|
199
|
-
/**
|
|
200
|
-
* Get optimization statistics
|
|
201
|
-
*/
|
|
202
|
-
getStats() {
|
|
203
|
-
const avgReward = this.totalSteps > 0 ? this.cumulativeReward / this.totalSteps : 0;
|
|
204
|
-
const avgTdError = this.rewardHistory.length > 0
|
|
205
|
-
? this.rewardHistory.reduce((sum, r) => sum + Math.abs(r), 0) /
|
|
206
|
-
this.rewardHistory.length
|
|
207
|
-
: 0;
|
|
208
|
-
return {
|
|
209
|
-
totalSteps: this.totalSteps,
|
|
210
|
-
episodes: this.episodes,
|
|
211
|
-
cumulativeReward: this.cumulativeReward,
|
|
212
|
-
avgReward,
|
|
213
|
-
avgTdError,
|
|
214
|
-
actionCounts: { ...this.actionCounts },
|
|
215
|
-
minCutHistory: [...this.minCutHistory],
|
|
216
|
-
rewardHistory: [...this.rewardHistory],
|
|
217
|
-
currentEpsilon: this.epsilon,
|
|
218
|
-
};
|
|
219
|
-
}
|
|
220
|
-
/**
|
|
221
|
-
* Reset optimizer state
|
|
222
|
-
*/
|
|
223
|
-
reset() {
|
|
224
|
-
this.prevState = this.extractFeatures();
|
|
225
|
-
this.prevMinCut = this.estimateMinCut();
|
|
226
|
-
this.time = 0;
|
|
227
|
-
this.epsilon = this.config.epsilon;
|
|
228
|
-
this.lastAction = null;
|
|
229
|
-
}
|
|
230
|
-
/**
|
|
231
|
-
* Hard reset (clear learning)
|
|
232
|
-
*/
|
|
233
|
-
hardReset() {
|
|
234
|
-
this.reset();
|
|
235
|
-
this.replayBuffer.clear();
|
|
236
|
-
this.totalSteps = 0;
|
|
237
|
-
this.episodes = 0;
|
|
238
|
-
this.cumulativeReward = 0;
|
|
239
|
-
this.minCutHistory = [];
|
|
240
|
-
this.rewardHistory = [];
|
|
241
|
-
for (const actionType of ACTION_TYPES) {
|
|
242
|
-
this.actionCounts[actionType] = 0;
|
|
243
|
-
}
|
|
244
|
-
// Reinitialize networks
|
|
245
|
-
this.valueNetwork = new ValueNetwork(this.config.inputSize, this.config.hiddenSize);
|
|
246
|
-
this.targetNetwork = new ValueNetwork(this.config.inputSize, this.config.hiddenSize);
|
|
247
|
-
this.targetNetwork.copyFrom(this.valueNetwork);
|
|
248
|
-
}
|
|
249
|
-
/**
|
|
250
|
-
* Export learned model
|
|
251
|
-
*/
|
|
252
|
-
exportModel() {
|
|
253
|
-
return {
|
|
254
|
-
type: 'neural-topology-optimizer',
|
|
255
|
-
version: '1.0.0',
|
|
256
|
-
config: { ...this.config },
|
|
257
|
-
valueNetwork: this.valueNetwork.export(),
|
|
258
|
-
targetNetwork: this.targetNetwork.export(),
|
|
259
|
-
stats: this.getStats(),
|
|
260
|
-
exportedAt: new Date().toISOString(),
|
|
261
|
-
};
|
|
262
|
-
}
|
|
263
|
-
/**
|
|
264
|
-
* Import learned model
|
|
265
|
-
*/
|
|
266
|
-
importModel(model) {
|
|
267
|
-
if (model.type !== 'neural-topology-optimizer') {
|
|
268
|
-
throw new Error(`Invalid model type: ${model.type}`);
|
|
269
|
-
}
|
|
270
|
-
// Import config
|
|
271
|
-
this.config = { ...DEFAULT_OPTIMIZER_CONFIG, ...model.config };
|
|
272
|
-
this.epsilon = model.stats.currentEpsilon;
|
|
273
|
-
// Import networks
|
|
274
|
-
this.valueNetwork = new ValueNetwork(this.config.inputSize, this.config.hiddenSize);
|
|
275
|
-
this.valueNetwork.import(model.valueNetwork);
|
|
276
|
-
if (model.targetNetwork) {
|
|
277
|
-
this.targetNetwork = new ValueNetwork(this.config.inputSize, this.config.hiddenSize);
|
|
278
|
-
this.targetNetwork.import(model.targetNetwork);
|
|
279
|
-
}
|
|
280
|
-
else {
|
|
281
|
-
this.targetNetwork.copyFrom(this.valueNetwork);
|
|
282
|
-
}
|
|
283
|
-
// Import stats
|
|
284
|
-
this.totalSteps = model.stats.totalSteps;
|
|
285
|
-
this.episodes = model.stats.episodes;
|
|
286
|
-
this.cumulativeReward = model.stats.cumulativeReward;
|
|
287
|
-
this.actionCounts = { ...model.stats.actionCounts };
|
|
288
|
-
this.minCutHistory = [...model.stats.minCutHistory];
|
|
289
|
-
this.rewardHistory = [...model.stats.rewardHistory];
|
|
290
|
-
}
|
|
291
|
-
// ============================================================================
|
|
292
|
-
// Private Methods
|
|
293
|
-
// ============================================================================
|
|
294
|
-
/**
|
|
295
|
-
* Extract features from topology for state representation
|
|
296
|
-
*/
|
|
297
|
-
extractFeatures() {
|
|
298
|
-
const n = this.topology.agents.length;
|
|
299
|
-
const m = this.topology.connections.length;
|
|
300
|
-
const loadStats = this.getLoadStats();
|
|
301
|
-
const state = {
|
|
302
|
-
agentCount: n,
|
|
303
|
-
connectionCount: m,
|
|
304
|
-
density: this.getDensity(),
|
|
305
|
-
avgDegree: this.getAverageDegree(),
|
|
306
|
-
minDegree: this.getMinDegree(),
|
|
307
|
-
avgWeight: this.getAverageWeight(),
|
|
308
|
-
weightVariance: this.getWeightVariance(),
|
|
309
|
-
avgLoad: loadStats.avg,
|
|
310
|
-
loadVariance: loadStats.variance,
|
|
311
|
-
avgLatency: this.getAverageLatency(),
|
|
312
|
-
idleAgents: loadStats.idle,
|
|
313
|
-
overloadedAgents: loadStats.overloaded,
|
|
314
|
-
clusteringCoefficient: this.getClusteringCoefficient(),
|
|
315
|
-
time: this.time,
|
|
316
|
-
extra: [],
|
|
317
|
-
};
|
|
318
|
-
// Convert to feature vector (normalized)
|
|
319
|
-
const features = new Array(this.config.inputSize).fill(0);
|
|
320
|
-
if (this.config.inputSize > 0)
|
|
321
|
-
features[0] = n / 100;
|
|
322
|
-
if (this.config.inputSize > 1)
|
|
323
|
-
features[1] = m / 500;
|
|
324
|
-
if (this.config.inputSize > 2)
|
|
325
|
-
features[2] = state.density;
|
|
326
|
-
if (this.config.inputSize > 3)
|
|
327
|
-
features[3] = state.avgDegree / 10;
|
|
328
|
-
if (this.config.inputSize > 4)
|
|
329
|
-
features[4] = state.minDegree / 10;
|
|
330
|
-
if (this.config.inputSize > 5)
|
|
331
|
-
features[5] = state.avgWeight / 5;
|
|
332
|
-
if (this.config.inputSize > 6)
|
|
333
|
-
features[6] = Math.min(1, state.weightVariance);
|
|
334
|
-
if (this.config.inputSize > 7)
|
|
335
|
-
features[7] = state.avgLoad;
|
|
336
|
-
if (this.config.inputSize > 8)
|
|
337
|
-
features[8] = Math.min(1, state.loadVariance);
|
|
338
|
-
if (this.config.inputSize > 9)
|
|
339
|
-
features[9] = state.avgLatency / 100;
|
|
340
|
-
if (this.config.inputSize > 10)
|
|
341
|
-
features[10] = state.idleAgents / Math.max(1, n);
|
|
342
|
-
if (this.config.inputSize > 11)
|
|
343
|
-
features[11] = state.overloadedAgents / Math.max(1, n);
|
|
344
|
-
if (this.config.inputSize > 12)
|
|
345
|
-
features[12] = state.clusteringCoefficient;
|
|
346
|
-
if (this.config.inputSize > 13)
|
|
347
|
-
features[13] = Math.sin(state.time * 0.1);
|
|
348
|
-
if (this.config.inputSize > 14)
|
|
349
|
-
features[14] = Math.cos(state.time * 0.1);
|
|
350
|
-
if (this.config.inputSize > 15)
|
|
351
|
-
features[15] = this.prevMinCut / Math.max(1, m);
|
|
352
|
-
return features;
|
|
353
|
-
}
|
|
354
|
-
/**
|
|
355
|
-
* Select action using epsilon-greedy policy
|
|
356
|
-
*/
|
|
357
|
-
selectAction(state) {
|
|
358
|
-
// Exploration: random action
|
|
359
|
-
if (secureRandom() < this.epsilon) {
|
|
360
|
-
return this.randomAction();
|
|
361
|
-
}
|
|
362
|
-
// Exploitation: select action with highest estimated value
|
|
363
|
-
let bestAction = { type: 'no_op' };
|
|
364
|
-
let bestValue = -Infinity;
|
|
365
|
-
for (let i = 0; i < this.config.numActions; i++) {
|
|
366
|
-
const action = this.indexToAction(i);
|
|
367
|
-
const hypotheticalState = this.simulateAction(state, action);
|
|
368
|
-
const value = this.valueNetwork.estimate(hypotheticalState);
|
|
369
|
-
if (value > bestValue) {
|
|
370
|
-
bestValue = value;
|
|
371
|
-
bestAction = action;
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
return bestAction;
|
|
375
|
-
}
|
|
376
|
-
/**
|
|
377
|
-
* Convert index to action
|
|
378
|
-
*/
|
|
379
|
-
indexToAction(idx) {
|
|
380
|
-
const agents = this.topology.agents;
|
|
381
|
-
if (agents.length < 2)
|
|
382
|
-
return { type: 'no_op' };
|
|
383
|
-
const actionType = indexToActionType(idx);
|
|
384
|
-
const from = agents[idx % agents.length].id;
|
|
385
|
-
const to = agents[(idx + 1) % agents.length].id;
|
|
386
|
-
switch (actionType) {
|
|
387
|
-
case 'add_connection':
|
|
388
|
-
if (!this.hasConnection(from, to)) {
|
|
389
|
-
return { type: 'add_connection', from, to, weight: 1.0 };
|
|
390
|
-
}
|
|
391
|
-
return { type: 'no_op' };
|
|
392
|
-
case 'remove_connection':
|
|
393
|
-
if (this.hasConnection(from, to)) {
|
|
394
|
-
return { type: 'remove_connection', from, to };
|
|
395
|
-
}
|
|
396
|
-
return { type: 'no_op' };
|
|
397
|
-
case 'strengthen_connection':
|
|
398
|
-
return { type: 'strengthen_connection', from, to, delta: 0.1 };
|
|
399
|
-
case 'weaken_connection':
|
|
400
|
-
return { type: 'weaken_connection', from, to, delta: 0.1 };
|
|
401
|
-
default:
|
|
402
|
-
return { type: 'no_op' };
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
/**
|
|
406
|
-
* Generate random action
|
|
407
|
-
*/
|
|
408
|
-
randomAction() {
|
|
409
|
-
const agents = this.topology.agents;
|
|
410
|
-
if (agents.length < 2)
|
|
411
|
-
return { type: 'no_op' };
|
|
412
|
-
const actionTypeIdx = secureRandomInt(0, ACTION_TYPES.length);
|
|
413
|
-
const actionType = ACTION_TYPES[actionTypeIdx];
|
|
414
|
-
const fromIdx = secureRandomInt(0, agents.length);
|
|
415
|
-
let toIdx = secureRandomInt(0, agents.length);
|
|
416
|
-
while (toIdx === fromIdx && agents.length > 1) {
|
|
417
|
-
toIdx = secureRandomInt(0, agents.length);
|
|
418
|
-
}
|
|
419
|
-
const from = agents[fromIdx].id;
|
|
420
|
-
const to = agents[toIdx].id;
|
|
421
|
-
switch (actionType) {
|
|
422
|
-
case 'add_connection':
|
|
423
|
-
if (!this.hasConnection(from, to)) {
|
|
424
|
-
return { type: 'add_connection', from, to, weight: 1.0 };
|
|
425
|
-
}
|
|
426
|
-
return { type: 'no_op' };
|
|
427
|
-
case 'remove_connection':
|
|
428
|
-
if (this.hasConnection(from, to)) {
|
|
429
|
-
return { type: 'remove_connection', from, to };
|
|
430
|
-
}
|
|
431
|
-
return { type: 'no_op' };
|
|
432
|
-
case 'strengthen_connection':
|
|
433
|
-
return { type: 'strengthen_connection', from, to, delta: 0.1 };
|
|
434
|
-
case 'weaken_connection':
|
|
435
|
-
return { type: 'weaken_connection', from, to, delta: 0.1 };
|
|
436
|
-
default:
|
|
437
|
-
return { type: 'no_op' };
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
/**
|
|
441
|
-
* Simulate action effect on state (for lookahead)
|
|
442
|
-
*/
|
|
443
|
-
simulateAction(state, action) {
|
|
444
|
-
const newState = [...state];
|
|
445
|
-
switch (action.type) {
|
|
446
|
-
case 'add_connection':
|
|
447
|
-
if (this.config.inputSize > 1)
|
|
448
|
-
newState[1] += 0.002; // connection count
|
|
449
|
-
if (this.config.inputSize > 2)
|
|
450
|
-
newState[2] += 0.01; // density
|
|
451
|
-
if (this.config.inputSize > 3)
|
|
452
|
-
newState[3] += 0.1; // avg degree
|
|
453
|
-
break;
|
|
454
|
-
case 'remove_connection':
|
|
455
|
-
if (this.config.inputSize > 1)
|
|
456
|
-
newState[1] -= 0.002;
|
|
457
|
-
if (this.config.inputSize > 2)
|
|
458
|
-
newState[2] -= 0.01;
|
|
459
|
-
if (this.config.inputSize > 3)
|
|
460
|
-
newState[3] -= 0.1;
|
|
461
|
-
break;
|
|
462
|
-
case 'strengthen_connection':
|
|
463
|
-
if (this.config.inputSize > 5)
|
|
464
|
-
newState[5] += 0.02; // avg weight
|
|
465
|
-
break;
|
|
466
|
-
case 'weaken_connection':
|
|
467
|
-
if (this.config.inputSize > 5)
|
|
468
|
-
newState[5] -= 0.02;
|
|
469
|
-
break;
|
|
470
|
-
}
|
|
471
|
-
return newState;
|
|
472
|
-
}
|
|
473
|
-
/**
|
|
474
|
-
* Apply action to topology
|
|
475
|
-
*/
|
|
476
|
-
applyAction(action) {
|
|
477
|
-
switch (action.type) {
|
|
478
|
-
case 'add_connection':
|
|
479
|
-
this.topology.addConnection(action.from, action.to, action.weight || 1.0);
|
|
480
|
-
break;
|
|
481
|
-
case 'remove_connection':
|
|
482
|
-
this.topology.removeConnection(action.from, action.to);
|
|
483
|
-
break;
|
|
484
|
-
case 'strengthen_connection':
|
|
485
|
-
this.topology.updateConnectionWeight(action.from, action.to, action.delta);
|
|
486
|
-
break;
|
|
487
|
-
case 'weaken_connection':
|
|
488
|
-
this.topology.updateConnectionWeight(action.from, action.to, -action.delta);
|
|
489
|
-
break;
|
|
490
|
-
case 'no_op':
|
|
491
|
-
// Do nothing
|
|
492
|
-
break;
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
/**
|
|
496
|
-
* Calculate multi-objective reward
|
|
497
|
-
*/
|
|
498
|
-
calculateReward(oldMinCut, newMinCut) {
|
|
499
|
-
// 1. Min-cut improvement (primary objective)
|
|
500
|
-
const minCutReward = oldMinCut > 0 ? (newMinCut - oldMinCut) / oldMinCut : 0;
|
|
501
|
-
// 2. Communication efficiency
|
|
502
|
-
const efficiency = this.measureCommunicationEfficiency();
|
|
503
|
-
// 3. Load balance
|
|
504
|
-
const loadStats = this.getLoadStats();
|
|
505
|
-
const loadBalance = 1 - loadStats.variance;
|
|
506
|
-
// 4. Latency penalty
|
|
507
|
-
const avgLatency = this.getAverageLatency();
|
|
508
|
-
const latencyPenalty = avgLatency > 0 ? -avgLatency / 100 : 0;
|
|
509
|
-
// Combine with weights
|
|
510
|
-
const reward = minCutReward +
|
|
511
|
-
this.config.efficiencyWeight * efficiency +
|
|
512
|
-
this.config.loadBalanceWeight * loadBalance +
|
|
513
|
-
this.config.latencyWeight * latencyPenalty;
|
|
514
|
-
return Math.max(-1, Math.min(1, reward));
|
|
515
|
-
}
|
|
516
|
-
/**
|
|
517
|
-
* Train from replay buffer
|
|
518
|
-
*/
|
|
519
|
-
trainFromReplay() {
|
|
520
|
-
const { experiences, weights, indices } = this.replayBuffer.sampleWithWeights(this.config.batchSize);
|
|
521
|
-
const newPriorities = [];
|
|
522
|
-
for (let i = 0; i < experiences.length; i++) {
|
|
523
|
-
const exp = experiences[i];
|
|
524
|
-
const weight = weights[i];
|
|
525
|
-
// Calculate TD error
|
|
526
|
-
const currentValue = this.valueNetwork.estimate(exp.state);
|
|
527
|
-
const nextValue = exp.done
|
|
528
|
-
? 0
|
|
529
|
-
: this.targetNetwork.estimate(exp.nextState);
|
|
530
|
-
const targetValue = exp.reward + this.config.gamma * nextValue;
|
|
531
|
-
const tdError = targetValue - currentValue;
|
|
532
|
-
// Weighted update
|
|
533
|
-
this.valueNetwork.update(exp.state, tdError * weight, this.config.learningRate);
|
|
534
|
-
newPriorities.push(Math.abs(tdError));
|
|
535
|
-
}
|
|
536
|
-
// Update priorities
|
|
537
|
-
this.replayBuffer.updatePriorities(indices, newPriorities);
|
|
538
|
-
}
|
|
539
|
-
// ============================================================================
|
|
540
|
-
// Topology Metric Helpers
|
|
541
|
-
// ============================================================================
|
|
542
|
-
estimateMinCut() {
|
|
543
|
-
if (this.topology.agents.length === 0)
|
|
544
|
-
return 0;
|
|
545
|
-
return this.getMinDegree();
|
|
546
|
-
}
|
|
547
|
-
hasConnection(from, to) {
|
|
548
|
-
return this.topology.connections.some((c) => (c.from === from && c.to === to) || (c.from === to && c.to === from));
|
|
549
|
-
}
|
|
550
|
-
getDensity() {
|
|
551
|
-
const n = this.topology.agents.length;
|
|
552
|
-
if (n < 2)
|
|
553
|
-
return 0;
|
|
554
|
-
const maxConnections = (n * (n - 1)) / 2;
|
|
555
|
-
return this.topology.connections.length / maxConnections;
|
|
556
|
-
}
|
|
557
|
-
getAverageDegree() {
|
|
558
|
-
const n = this.topology.agents.length;
|
|
559
|
-
if (n === 0)
|
|
560
|
-
return 0;
|
|
561
|
-
const totalDegree = this.topology.agents.reduce((sum, agent) => {
|
|
562
|
-
return (sum +
|
|
563
|
-
this.topology.connections.filter((c) => c.from === agent.id || c.to === agent.id).length);
|
|
564
|
-
}, 0);
|
|
565
|
-
return totalDegree / n;
|
|
566
|
-
}
|
|
567
|
-
getMinDegree() {
|
|
568
|
-
if (this.topology.agents.length === 0)
|
|
569
|
-
return 0;
|
|
570
|
-
let minDegree = Infinity;
|
|
571
|
-
for (const agent of this.topology.agents) {
|
|
572
|
-
const degree = this.topology.connections.filter((c) => c.from === agent.id || c.to === agent.id).length;
|
|
573
|
-
minDegree = Math.min(minDegree, degree);
|
|
574
|
-
}
|
|
575
|
-
return minDegree === Infinity ? 0 : minDegree;
|
|
576
|
-
}
|
|
577
|
-
getAverageWeight() {
|
|
578
|
-
if (this.topology.connections.length === 0)
|
|
579
|
-
return 0;
|
|
580
|
-
const total = this.topology.connections.reduce((sum, c) => sum + c.weight, 0);
|
|
581
|
-
return total / this.topology.connections.length;
|
|
582
|
-
}
|
|
583
|
-
getWeightVariance() {
|
|
584
|
-
if (this.topology.connections.length === 0)
|
|
585
|
-
return 0;
|
|
586
|
-
const avg = this.getAverageWeight();
|
|
587
|
-
const squaredDiffs = this.topology.connections.map((c) => (c.weight - avg) ** 2);
|
|
588
|
-
return squaredDiffs.reduce((sum, d) => sum + d, 0) / this.topology.connections.length;
|
|
589
|
-
}
|
|
590
|
-
getAverageLatency() {
|
|
591
|
-
const connectionsWithLatency = this.topology.connections.filter((c) => c.latencyMs !== undefined);
|
|
592
|
-
if (connectionsWithLatency.length === 0)
|
|
593
|
-
return 0;
|
|
594
|
-
const total = connectionsWithLatency.reduce((sum, c) => sum + (c.latencyMs || 0), 0);
|
|
595
|
-
return total / connectionsWithLatency.length;
|
|
596
|
-
}
|
|
597
|
-
measureCommunicationEfficiency() {
|
|
598
|
-
const n = this.topology.agents.length;
|
|
599
|
-
if (n < 2)
|
|
600
|
-
return 0;
|
|
601
|
-
const maxConnections = (n * (n - 1)) / 2;
|
|
602
|
-
return this.topology.connections.length / maxConnections;
|
|
603
|
-
}
|
|
604
|
-
getClusteringCoefficient() {
|
|
605
|
-
if (this.topology.agents.length < 3)
|
|
606
|
-
return 0;
|
|
607
|
-
let totalCoeff = 0;
|
|
608
|
-
for (const agent of this.topology.agents) {
|
|
609
|
-
const neighbors = this.getNeighbors(agent.id);
|
|
610
|
-
const k = neighbors.length;
|
|
611
|
-
if (k < 2)
|
|
612
|
-
continue;
|
|
613
|
-
let triangles = 0;
|
|
614
|
-
for (let i = 0; i < neighbors.length; i++) {
|
|
615
|
-
for (let j = i + 1; j < neighbors.length; j++) {
|
|
616
|
-
if (this.hasConnection(neighbors[i], neighbors[j])) {
|
|
617
|
-
triangles++;
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
const possibleTriangles = (k * (k - 1)) / 2;
|
|
622
|
-
totalCoeff += triangles / possibleTriangles;
|
|
623
|
-
}
|
|
624
|
-
return totalCoeff / this.topology.agents.length;
|
|
625
|
-
}
|
|
626
|
-
getNeighbors(agentId) {
|
|
627
|
-
const neighbors = [];
|
|
628
|
-
for (const conn of this.topology.connections) {
|
|
629
|
-
if (conn.from === agentId)
|
|
630
|
-
neighbors.push(conn.to);
|
|
631
|
-
else if (conn.to === agentId)
|
|
632
|
-
neighbors.push(conn.from);
|
|
633
|
-
}
|
|
634
|
-
return neighbors;
|
|
635
|
-
}
|
|
636
|
-
getLoadStats() {
|
|
637
|
-
if (this.topology.agents.length === 0) {
|
|
638
|
-
return { avg: 0, variance: 0, idle: 0, overloaded: 0 };
|
|
639
|
-
}
|
|
640
|
-
const loads = this.topology.agents.map((a) => a.metrics?.currentLoad ?? 0);
|
|
641
|
-
const avg = loads.reduce((sum, l) => sum + l, 0) / loads.length;
|
|
642
|
-
const variance = loads.reduce((sum, l) => sum + (l - avg) ** 2, 0) / loads.length;
|
|
643
|
-
const idle = this.topology.agents.filter((a) => a.status === 'idle').length;
|
|
644
|
-
const overloaded = this.topology.agents.filter((a) => (a.metrics?.currentLoad ?? 0) > 0.8).length;
|
|
645
|
-
return { avg, variance, idle, overloaded };
|
|
646
|
-
}
|
|
647
|
-
}
|
|
648
|
-
// ============================================================================
|
|
649
|
-
// Factory Function
|
|
650
|
-
// ============================================================================
|
|
651
|
-
/**
|
|
652
|
-
* Create a neural topology optimizer
|
|
653
|
-
*/
|
|
654
|
-
export function createNeuralTopologyOptimizer(topology, config) {
|
|
655
|
-
return new NeuralTopologyOptimizer(topology, config);
|
|
656
|
-
}
|
|
657
|
-
//# sourceMappingURL=topology-optimizer.js.map
|