@cnstra/core 1.8.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -612
- package/dist/CNS.d.ts +6 -9
- package/dist/CNS.d.ts.map +1 -1
- package/dist/CNSCollateral.d.ts +3 -5
- package/dist/CNSCollateral.d.ts.map +1 -1
- package/dist/CNSInstanceNeuronQueue.d.ts +0 -1
- package/dist/CNSInstanceNeuronQueue.d.ts.map +1 -1
- package/dist/CNSNetwork.d.ts +16 -20
- package/dist/CNSNetwork.d.ts.map +1 -1
- package/dist/CNSNeuronActivationPump.d.ts +5 -5
- package/dist/CNSNeuronActivationPump.d.ts.map +1 -1
- package/dist/CNSStimulation.d.ts +16 -15
- package/dist/CNSStimulation.d.ts.map +1 -1
- package/dist/CNSStimulationContextStore.d.ts +6 -6
- package/dist/CNSStimulationContextStore.d.ts.map +1 -1
- package/dist/factory/index.d.ts +97 -14
- package/dist/factory/index.d.ts.map +1 -1
- package/dist/index.cjs +235 -197
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +234 -198
- package/dist/index.js.map +1 -1
- package/dist/interfaces/ICNS.d.ts +5 -4
- package/dist/interfaces/ICNS.d.ts.map +1 -1
- package/dist/interfaces/ICNSCollateral.d.ts +3 -3
- package/dist/interfaces/ICNSCollateral.d.ts.map +1 -1
- package/dist/interfaces/ICNSStimulationContextStore.d.ts +5 -6
- package/dist/interfaces/ICNSStimulationContextStore.d.ts.map +1 -1
- package/dist/types/TCNSAfferentPath.d.ts +4 -0
- package/dist/types/TCNSAfferentPath.d.ts.map +1 -0
- package/dist/types/TCNSAxon.d.ts +23 -2
- package/dist/types/TCNSAxon.d.ts.map +1 -1
- package/dist/types/TCNSDendrite.d.ts +6 -6
- package/dist/types/TCNSDendrite.d.ts.map +1 -1
- package/dist/types/TCNSModality.d.ts +5 -0
- package/dist/types/TCNSModality.d.ts.map +1 -0
- package/dist/types/TCNSNeuron.d.ts +3 -3
- package/dist/types/TCNSNeuron.d.ts.map +1 -1
- package/dist/types/TCNSNeuronActivationTask.d.ts +5 -5
- package/dist/types/TCNSNeuronActivationTask.d.ts.map +1 -1
- package/dist/types/TCNSNeuronActivationTaskFailure.d.ts +2 -2
- package/dist/types/TCNSNeuronActivationTaskFailure.d.ts.map +1 -1
- package/dist/types/TCNSNeuronResponseReturn.d.ts +8 -2
- package/dist/types/TCNSNeuronResponseReturn.d.ts.map +1 -1
- package/dist/types/TCNSPersist.d.ts +16 -0
- package/dist/types/TCNSPersist.d.ts.map +1 -0
- package/dist/types/TCNSSignal.d.ts +4 -3
- package/dist/types/TCNSSignal.d.ts.map +1 -1
- package/dist/types/TCNSStimulationOptions.d.ts +9 -9
- package/dist/types/TCNSStimulationOptions.d.ts.map +1 -1
- package/dist/types/TCNSStimulationQueueItem.d.ts +4 -3
- package/dist/types/TCNSStimulationQueueItem.d.ts.map +1 -1
- package/dist/types/TCNSStimulationResponse.d.ts +10 -11
- package/dist/types/TCNSStimulationResponse.d.ts.map +1 -1
- package/dist/types/TCNSSubscriber.d.ts +1 -1
- package/dist/types/TCNSSubscriber.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -10,12 +10,11 @@ var CNSStimulationContextStore = class {
|
|
|
10
10
|
this.ctx.set(key, value);
|
|
11
11
|
}
|
|
12
12
|
getAll() {
|
|
13
|
-
return
|
|
13
|
+
return new Map(this.ctx);
|
|
14
14
|
}
|
|
15
15
|
setAll(values) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
16
|
+
this.ctx.clear();
|
|
17
|
+
for (const [key, value] of values) this.ctx.set(key, value);
|
|
19
18
|
}
|
|
20
19
|
delete(key) {
|
|
21
20
|
this.ctx.delete(key);
|
|
@@ -162,10 +161,7 @@ var CNSStimulation = class {
|
|
|
162
161
|
* Track active SCCs: SCC index -> count of active neurons in that SCC
|
|
163
162
|
*/
|
|
164
163
|
this.activeSccCounts = /* @__PURE__ */ new Map();
|
|
165
|
-
this.ctx = new CNSStimulationContextStore(
|
|
166
|
-
new Map(Object.entries(options?.contextValues ?? {}))
|
|
167
|
-
);
|
|
168
|
-
this.id = options?.stimulationId || Math.random().toString(36).slice(2);
|
|
164
|
+
this.ctx = options?.ctx ?? new CNSStimulationContextStore();
|
|
169
165
|
this.queue = new CNSNeuronActivationPump(
|
|
170
166
|
(neuronActivationTask) => this.executeActivationTask(neuronActivationTask),
|
|
171
167
|
options?.concurrency,
|
|
@@ -186,7 +182,7 @@ var CNSStimulation = class {
|
|
|
186
182
|
const queuedTasks = this.queue.getQueuedTasks();
|
|
187
183
|
for (const task of [...queuedTasks]) {
|
|
188
184
|
const alreadyTracked = this.failedTasks.some(
|
|
189
|
-
(ft) => ft.task
|
|
185
|
+
(ft) => ft.task === task
|
|
190
186
|
);
|
|
191
187
|
if (!alreadyTracked) {
|
|
192
188
|
this.failedTasks.push({
|
|
@@ -242,11 +238,7 @@ var CNSStimulation = class {
|
|
|
242
238
|
*/
|
|
243
239
|
enqueueTasks(tasks) {
|
|
244
240
|
for (const task of tasks) {
|
|
245
|
-
|
|
246
|
-
...task,
|
|
247
|
-
stimulationId: this.id
|
|
248
|
-
};
|
|
249
|
-
this.queue.enqueue(taskWithId);
|
|
241
|
+
this.queue.enqueue(task);
|
|
250
242
|
}
|
|
251
243
|
this.tryResolveCompleted();
|
|
252
244
|
}
|
|
@@ -259,22 +251,22 @@ var CNSStimulation = class {
|
|
|
259
251
|
/**
|
|
260
252
|
* Mark a neuron as active (being processed)
|
|
261
253
|
*/
|
|
262
|
-
markNeuronActive(
|
|
254
|
+
markNeuronActive(neuron2) {
|
|
263
255
|
if (!this.autoCleanupContextsEnabled) return;
|
|
264
|
-
this.incrementSccCount(
|
|
256
|
+
this.incrementSccCount(neuron2);
|
|
265
257
|
}
|
|
266
258
|
/**
|
|
267
259
|
* Mark a neuron as inactive (finished processing)
|
|
268
260
|
*/
|
|
269
|
-
markNeuronInactive(
|
|
261
|
+
markNeuronInactive(neuron2) {
|
|
270
262
|
if (!this.autoCleanupContextsEnabled) return;
|
|
271
|
-
this.decrementSccCount(
|
|
263
|
+
this.decrementSccCount(neuron2);
|
|
272
264
|
}
|
|
273
265
|
/**
|
|
274
266
|
* Increment the active count for the SCC containing this neuron
|
|
275
267
|
*/
|
|
276
|
-
incrementSccCount(
|
|
277
|
-
const sccIndex = this.cns.network.
|
|
268
|
+
incrementSccCount(neuron2) {
|
|
269
|
+
const sccIndex = this.cns.network.getSccIndexByNeuron(neuron2);
|
|
278
270
|
if (sccIndex === void 0) return;
|
|
279
271
|
const currentCount = this.activeSccCounts.get(sccIndex) || 0;
|
|
280
272
|
this.activeSccCounts.set(sccIndex, currentCount + 1);
|
|
@@ -282,66 +274,65 @@ var CNSStimulation = class {
|
|
|
282
274
|
/**
|
|
283
275
|
* Decrement the active count for the SCC containing this neuron
|
|
284
276
|
*/
|
|
285
|
-
decrementSccCount(
|
|
286
|
-
const sccIndex = this.cns.network.
|
|
277
|
+
decrementSccCount(neuron2) {
|
|
278
|
+
const sccIndex = this.cns.network.getSccIndexByNeuron(neuron2);
|
|
287
279
|
if (sccIndex === void 0) return;
|
|
288
280
|
const currentCount = this.activeSccCounts.get(sccIndex) || 0;
|
|
289
281
|
const nextCount = Math.max(0, currentCount - 1);
|
|
290
282
|
this.activeSccCounts.set(sccIndex, nextCount);
|
|
291
283
|
if (currentCount === 0) {
|
|
292
284
|
console.warn(
|
|
293
|
-
`[CNSStimulation] Attempting to decrement SCC count below 0 for neuron
|
|
285
|
+
`[CNSStimulation] Attempting to decrement SCC count below 0 for neuron`
|
|
294
286
|
);
|
|
295
287
|
}
|
|
296
288
|
}
|
|
297
289
|
/**
|
|
298
290
|
* Check if a neuron can be guaranteed not to be visited again
|
|
299
291
|
*/
|
|
300
|
-
canNeuronBeGuaranteedDone(
|
|
292
|
+
canNeuronBeGuaranteedDone(neuron2) {
|
|
301
293
|
if (!this.autoCleanupContextsEnabled) return false;
|
|
302
294
|
return this.cns.network.canNeuronBeGuaranteedDone(
|
|
303
|
-
|
|
295
|
+
neuron2,
|
|
304
296
|
this.activeSccCounts
|
|
305
297
|
);
|
|
306
298
|
}
|
|
307
299
|
cleanupCtxIfNeeded(neuron2) {
|
|
308
|
-
if (this.autoCleanupContextsEnabled && this.canNeuronBeGuaranteedDone(neuron2
|
|
309
|
-
this.ctx.delete(neuron2
|
|
300
|
+
if (this.autoCleanupContextsEnabled && this.canNeuronBeGuaranteedDone(neuron2)) {
|
|
301
|
+
this.ctx.delete(neuron2);
|
|
310
302
|
}
|
|
311
303
|
}
|
|
312
304
|
createSubscriberQueueItem(subscriber, inputSignal) {
|
|
313
305
|
if (this.options?.maxNeuronHops) {
|
|
314
|
-
if (this.nueronVisitMap?.get(subscriber.neuron
|
|
306
|
+
if ((this.nueronVisitMap?.get(subscriber.neuron) ?? 0) >= this.options?.maxNeuronHops) {
|
|
315
307
|
throw new Error(
|
|
316
|
-
`Max neuron hops reached
|
|
308
|
+
`Max neuron hops reached when trying to enqueue subscriber`
|
|
317
309
|
);
|
|
318
310
|
} else {
|
|
319
311
|
this.nueronVisitMap?.set(
|
|
320
|
-
subscriber.neuron
|
|
321
|
-
(this.nueronVisitMap?.get(subscriber.neuron
|
|
312
|
+
subscriber.neuron,
|
|
313
|
+
(this.nueronVisitMap?.get(subscriber.neuron) ?? 0) + 1
|
|
322
314
|
);
|
|
323
315
|
}
|
|
324
316
|
}
|
|
325
317
|
const neuronActivationTask = {
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
dendriteCollateralName: subscriber.dendrite.collateral.name,
|
|
318
|
+
neuron: subscriber.neuron,
|
|
319
|
+
dendriteCollateral: subscriber.dendrite.collateral,
|
|
329
320
|
input: inputSignal
|
|
330
321
|
};
|
|
331
322
|
return neuronActivationTask;
|
|
332
323
|
}
|
|
333
324
|
executeActivationTask(neuronActivationTask) {
|
|
334
325
|
const subscribers = this.cns.network.getSubscribers(
|
|
335
|
-
neuronActivationTask.
|
|
326
|
+
neuronActivationTask.dendriteCollateral
|
|
336
327
|
);
|
|
337
328
|
const subscriber = subscribers.find(
|
|
338
|
-
(s) => s.neuron
|
|
329
|
+
(s) => s.neuron === neuronActivationTask.neuron
|
|
339
330
|
);
|
|
340
331
|
if (!subscriber) {
|
|
341
332
|
this.failedTasks.push({
|
|
342
333
|
task: neuronActivationTask,
|
|
343
334
|
error: new Error(
|
|
344
|
-
`Subscriber not found for
|
|
335
|
+
`Subscriber not found for activation task`
|
|
345
336
|
),
|
|
346
337
|
aborted: false
|
|
347
338
|
});
|
|
@@ -350,28 +341,25 @@ var CNSStimulation = class {
|
|
|
350
341
|
}
|
|
351
342
|
const neuron2 = subscriber.neuron;
|
|
352
343
|
const dendrite = subscriber.dendrite;
|
|
353
|
-
const inputSignal = neuronActivationTask.input
|
|
354
|
-
collateralName: neuronActivationTask.input.collateralName,
|
|
355
|
-
payload: neuronActivationTask.input.payload
|
|
356
|
-
} : void 0;
|
|
344
|
+
const inputSignal = neuronActivationTask.input;
|
|
357
345
|
const starter = () => {
|
|
358
|
-
this.markNeuronActive(neuron2
|
|
346
|
+
this.markNeuronActive(neuron2);
|
|
359
347
|
let response;
|
|
360
348
|
try {
|
|
361
349
|
response = dendrite.response(
|
|
362
350
|
inputSignal?.payload,
|
|
363
351
|
neuron2.axon,
|
|
364
352
|
{
|
|
365
|
-
get: () => this.ctx.get(neuron2
|
|
366
|
-
set: (value) => this.ctx.set(neuron2
|
|
367
|
-
delete: () => this.ctx.delete(neuron2
|
|
353
|
+
get: () => this.ctx.get(neuron2),
|
|
354
|
+
set: (value) => this.ctx.set(neuron2, value),
|
|
355
|
+
delete: () => this.ctx.delete(neuron2),
|
|
368
356
|
abortSignal: this.options?.abortSignal,
|
|
369
357
|
cns: this.cns,
|
|
370
|
-
|
|
358
|
+
stimulation: this
|
|
371
359
|
}
|
|
372
360
|
);
|
|
373
361
|
} catch (error) {
|
|
374
|
-
this.markNeuronInactive(neuron2
|
|
362
|
+
this.markNeuronInactive(neuron2);
|
|
375
363
|
const isAborted = this.options?.abortSignal?.aborted ?? false;
|
|
376
364
|
this.failedTasks.push({
|
|
377
365
|
task: neuronActivationTask,
|
|
@@ -379,6 +367,7 @@ var CNSStimulation = class {
|
|
|
379
367
|
aborted: isAborted
|
|
380
368
|
});
|
|
381
369
|
this.processResponseOrResponses(
|
|
370
|
+
neuron2,
|
|
382
371
|
inputSignal,
|
|
383
372
|
void 0,
|
|
384
373
|
error
|
|
@@ -391,9 +380,7 @@ var CNSStimulation = class {
|
|
|
391
380
|
const timedPromise = maxDuration && maxDuration > 0 ? new Promise((resolve, reject) => {
|
|
392
381
|
const t = setTimeout(() => {
|
|
393
382
|
const err = new Error(
|
|
394
|
-
`Neuron
|
|
395
|
-
neuron2.name
|
|
396
|
-
)}" exceeded maxDuration ${maxDuration}ms`
|
|
383
|
+
`Neuron exceeded maxDuration ${maxDuration}ms`
|
|
397
384
|
);
|
|
398
385
|
reject(err);
|
|
399
386
|
}, maxDuration);
|
|
@@ -412,8 +399,9 @@ var CNSStimulation = class {
|
|
|
412
399
|
return timedPromise.then(
|
|
413
400
|
(signal) => {
|
|
414
401
|
return () => {
|
|
415
|
-
this.markNeuronInactive(neuron2
|
|
402
|
+
this.markNeuronInactive(neuron2);
|
|
416
403
|
return this.processResponseOrResponses(
|
|
404
|
+
neuron2,
|
|
417
405
|
inputSignal,
|
|
418
406
|
signal
|
|
419
407
|
);
|
|
@@ -421,7 +409,7 @@ var CNSStimulation = class {
|
|
|
421
409
|
},
|
|
422
410
|
(error) => {
|
|
423
411
|
return () => {
|
|
424
|
-
this.markNeuronInactive(neuron2
|
|
412
|
+
this.markNeuronInactive(neuron2);
|
|
425
413
|
const isAborted = this.options?.abortSignal?.aborted ?? false;
|
|
426
414
|
this.failedTasks.push({
|
|
427
415
|
task: neuronActivationTask,
|
|
@@ -429,6 +417,7 @@ var CNSStimulation = class {
|
|
|
429
417
|
aborted: isAborted
|
|
430
418
|
});
|
|
431
419
|
return this.processResponseOrResponses(
|
|
420
|
+
neuron2,
|
|
432
421
|
inputSignal,
|
|
433
422
|
void 0,
|
|
434
423
|
error
|
|
@@ -438,8 +427,9 @@ var CNSStimulation = class {
|
|
|
438
427
|
);
|
|
439
428
|
} else {
|
|
440
429
|
return () => {
|
|
441
|
-
this.markNeuronInactive(neuron2
|
|
430
|
+
this.markNeuronInactive(neuron2);
|
|
442
431
|
return this.processResponseOrResponses(
|
|
432
|
+
neuron2,
|
|
443
433
|
inputSignal,
|
|
444
434
|
response
|
|
445
435
|
);
|
|
@@ -448,28 +438,25 @@ var CNSStimulation = class {
|
|
|
448
438
|
};
|
|
449
439
|
return this.instanceNeuronQueue.run(neuron2, starter);
|
|
450
440
|
}
|
|
451
|
-
processResponseOrResponses(inputSignal, outputSignalOrSignals, error) {
|
|
441
|
+
processResponseOrResponses(emitter, inputSignal, outputSignalOrSignals, error) {
|
|
452
442
|
if (Array.isArray(outputSignalOrSignals)) {
|
|
453
443
|
if (outputSignalOrSignals.length === 0) {
|
|
454
|
-
this.processResponse(inputSignal, void 0, error);
|
|
444
|
+
this.processResponse(emitter, inputSignal, void 0, error);
|
|
455
445
|
return;
|
|
456
446
|
}
|
|
457
447
|
for (const signal of outputSignalOrSignals) {
|
|
458
|
-
this.processResponse(inputSignal, signal, error);
|
|
448
|
+
this.processResponse(emitter, inputSignal, signal, error);
|
|
459
449
|
}
|
|
460
450
|
return;
|
|
461
451
|
}
|
|
462
|
-
this.processResponse(inputSignal, outputSignalOrSignals, error);
|
|
452
|
+
this.processResponse(emitter, inputSignal, outputSignalOrSignals, error);
|
|
463
453
|
}
|
|
464
|
-
processResponse(inputSignal, outputSignal, error) {
|
|
465
|
-
const
|
|
466
|
-
const
|
|
467
|
-
const subscribers = collateralName ? this.cns.network.getSubscribers(collateralName) : [];
|
|
454
|
+
processResponse(emitter, inputSignal, outputSignal, error) {
|
|
455
|
+
const collateral2 = outputSignal?.collateral;
|
|
456
|
+
const subscribers = collateral2 ? this.cns.network.getSubscribers(collateral2) : [];
|
|
468
457
|
const subscriberActivationTasks = [];
|
|
469
|
-
if (
|
|
470
|
-
if (
|
|
471
|
-
this.cleanupCtxIfNeeded(ownerNeuron);
|
|
472
|
-
}
|
|
458
|
+
if (collateral2 && !error) {
|
|
459
|
+
if (emitter) this.cleanupCtxIfNeeded(emitter);
|
|
473
460
|
for (let i = 0; i < subscribers.length; i++) {
|
|
474
461
|
const task = this.createSubscriberQueueItem(
|
|
475
462
|
subscribers[i],
|
|
@@ -488,8 +475,7 @@ var CNSStimulation = class {
|
|
|
488
475
|
contextValue: this.ctx.getAll(),
|
|
489
476
|
queueLength: this.queue.length + this.scheduledCount,
|
|
490
477
|
stimulation: this,
|
|
491
|
-
|
|
492
|
-
hops: this.options?.maxNeuronHops && ownerNeuron ? this.nueronVisitMap?.get(ownerNeuron.name) ?? 0 : void 0,
|
|
478
|
+
hops: this.options?.maxNeuronHops && emitter ? this.nueronVisitMap?.get(emitter) ?? 0 : void 0,
|
|
493
479
|
error
|
|
494
480
|
});
|
|
495
481
|
} catch (e) {
|
|
@@ -533,24 +519,10 @@ var CNSStimulation = class {
|
|
|
533
519
|
}
|
|
534
520
|
responseToSignal(signalOrSignals) {
|
|
535
521
|
if (Array.isArray(signalOrSignals)) {
|
|
536
|
-
|
|
537
|
-
const ownerNeuron2 = this.cns.network.getParentNeuronByCollateralName(
|
|
538
|
-
signal.collateralName
|
|
539
|
-
);
|
|
540
|
-
if (ownerNeuron2) {
|
|
541
|
-
this.markNeuronActive(ownerNeuron2.name);
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
this.processResponseOrResponses(void 0, signalOrSignals);
|
|
522
|
+
this.processResponseOrResponses(void 0, void 0, signalOrSignals);
|
|
545
523
|
return;
|
|
546
524
|
}
|
|
547
|
-
|
|
548
|
-
signalOrSignals.collateralName
|
|
549
|
-
);
|
|
550
|
-
if (ownerNeuron) {
|
|
551
|
-
this.markNeuronActive(ownerNeuron.name);
|
|
552
|
-
}
|
|
553
|
-
this.processResponse(void 0, signalOrSignals);
|
|
525
|
+
this.processResponse(void 0, void 0, signalOrSignals);
|
|
554
526
|
this.tryResolveCompleted();
|
|
555
527
|
}
|
|
556
528
|
};
|
|
@@ -565,10 +537,10 @@ var CNSInstanceNeuronQueue = class {
|
|
|
565
537
|
if (limit === void 0 || limit <= 0) {
|
|
566
538
|
return fn();
|
|
567
539
|
}
|
|
568
|
-
let gate = this.gates.get(neuron2
|
|
540
|
+
let gate = this.gates.get(neuron2);
|
|
569
541
|
if (!gate) {
|
|
570
542
|
gate = { limit, active: 0, waiters: [] };
|
|
571
|
-
this.gates.set(neuron2
|
|
543
|
+
this.gates.set(neuron2, gate);
|
|
572
544
|
} else {
|
|
573
545
|
gate.limit = limit;
|
|
574
546
|
}
|
|
@@ -577,7 +549,7 @@ var CNSInstanceNeuronQueue = class {
|
|
|
577
549
|
try {
|
|
578
550
|
cb();
|
|
579
551
|
} finally {
|
|
580
|
-
const g = this.gates.get(neuron2
|
|
552
|
+
const g = this.gates.get(neuron2);
|
|
581
553
|
g.active = Math.max(0, g.active - 1);
|
|
582
554
|
if (g.waiters.length > 0) {
|
|
583
555
|
const next = g.waiters.shift();
|
|
@@ -596,7 +568,7 @@ var CNSInstanceNeuronQueue = class {
|
|
|
596
568
|
}
|
|
597
569
|
return new Promise((resolve) => {
|
|
598
570
|
const starter = () => {
|
|
599
|
-
const g = this.gates.get(neuron2
|
|
571
|
+
const g = this.gates.get(neuron2);
|
|
600
572
|
g.active++;
|
|
601
573
|
const ret = fn();
|
|
602
574
|
if (ret && typeof ret.then === "function") {
|
|
@@ -616,11 +588,11 @@ var CNSNetwork = class {
|
|
|
616
588
|
this.neurons = neurons;
|
|
617
589
|
/**
|
|
618
590
|
* Strongly Connected Components of the neuron graph.
|
|
619
|
-
* Each component is a set of neuron
|
|
591
|
+
* Each component is a set of neuron objects that can reach each other.
|
|
620
592
|
*/
|
|
621
593
|
this.stronglyConnectedComponents = [];
|
|
622
594
|
/**
|
|
623
|
-
* Quick lookup: neuron
|
|
595
|
+
* Quick lookup: neuron -> SCC index for fast reachability checks.
|
|
624
596
|
*/
|
|
625
597
|
this.neuronToSCC = /* @__PURE__ */ new Map();
|
|
626
598
|
/**
|
|
@@ -632,68 +604,48 @@ var CNSNetwork = class {
|
|
|
632
604
|
*/
|
|
633
605
|
this.sccAncestors = /* @__PURE__ */ new Map();
|
|
634
606
|
/**
|
|
635
|
-
* Fast lookup:
|
|
607
|
+
* Fast lookup: collateral -> list of (neuron, dendrite) subscribers.
|
|
636
608
|
* Built once at construction time.
|
|
637
609
|
*/
|
|
638
610
|
this.subIndex = /* @__PURE__ */ new Map();
|
|
639
|
-
this.neuronIndex = /* @__PURE__ */ new Map();
|
|
640
611
|
this.dendriteIndex = /* @__PURE__ */ new Map();
|
|
641
|
-
this.collateralIndex = /* @__PURE__ */ new
|
|
642
|
-
this.
|
|
612
|
+
this.collateralIndex = /* @__PURE__ */ new Set();
|
|
613
|
+
this.parentNeuronByCollateral = /* @__PURE__ */ new Map();
|
|
643
614
|
this.validateUniqueIdentifiers();
|
|
644
615
|
this.buildIndexes();
|
|
645
616
|
this.buildSCC();
|
|
646
617
|
}
|
|
647
618
|
/**
|
|
648
619
|
* Validate uniqueness constraints:
|
|
649
|
-
* -
|
|
650
|
-
*
|
|
651
|
-
*
|
|
620
|
+
* - (legacy) collateral instances used to be single-owner; in object-identity routing
|
|
621
|
+
* a collateral can be emitted by multiple neurons (fan-in), so we no longer
|
|
622
|
+
* enforce single ownership here.
|
|
652
623
|
* Throws aggregated error if violations are found.
|
|
653
624
|
*/
|
|
654
625
|
validateUniqueIdentifiers() {
|
|
655
|
-
const errors = [];
|
|
656
|
-
const seenNeuronNames = /* @__PURE__ */ new Set();
|
|
657
|
-
for (const neuron2 of this.neurons) {
|
|
658
|
-
const name = neuron2?.name ?? "";
|
|
659
|
-
if (!name || typeof name !== "string" || name.trim().length === 0) {
|
|
660
|
-
errors.push(`Neuron has empty or invalid name: "${name}"`);
|
|
661
|
-
} else if (seenNeuronNames.has(name)) {
|
|
662
|
-
errors.push(`Duplicate neuron name: "${name}"`);
|
|
663
|
-
} else {
|
|
664
|
-
seenNeuronNames.add(name);
|
|
665
|
-
}
|
|
666
|
-
}
|
|
667
|
-
if (errors.length > 0) {
|
|
668
|
-
throw new Error(
|
|
669
|
-
`[CNS] Topology uniqueness validation failed:
|
|
670
|
-
- ` + errors.join(`
|
|
671
|
-
- `)
|
|
672
|
-
);
|
|
673
|
-
}
|
|
674
626
|
}
|
|
675
627
|
/**
|
|
676
628
|
* Build the neuron graph where neurons are connected if one can stimulate the other.
|
|
677
629
|
*/
|
|
678
630
|
buildNeuronGraph() {
|
|
679
631
|
const graph = /* @__PURE__ */ new Map();
|
|
680
|
-
const
|
|
681
|
-
|
|
682
|
-
graph.set(neuronId, /* @__PURE__ */ new Set());
|
|
632
|
+
for (const neuron2 of this.neurons) {
|
|
633
|
+
graph.set(neuron2, /* @__PURE__ */ new Set());
|
|
683
634
|
}
|
|
684
635
|
for (const neuron2 of this.neurons) {
|
|
685
636
|
const reachableNeurons = /* @__PURE__ */ new Set();
|
|
686
637
|
for (const axonKey in neuron2.axon) {
|
|
687
638
|
const collateral2 = neuron2.axon[axonKey];
|
|
688
|
-
const
|
|
689
|
-
|
|
639
|
+
const subscribers = this.subIndex.get(
|
|
640
|
+
collateral2
|
|
641
|
+
);
|
|
690
642
|
if (subscribers) {
|
|
691
643
|
for (const { neuron: targetNeuron } of subscribers) {
|
|
692
|
-
reachableNeurons.add(targetNeuron
|
|
644
|
+
reachableNeurons.add(targetNeuron);
|
|
693
645
|
}
|
|
694
646
|
}
|
|
695
647
|
}
|
|
696
|
-
graph.set(neuron2
|
|
648
|
+
graph.set(neuron2, reachableNeurons);
|
|
697
649
|
}
|
|
698
650
|
return graph;
|
|
699
651
|
}
|
|
@@ -702,48 +654,47 @@ var CNSNetwork = class {
|
|
|
702
654
|
*/
|
|
703
655
|
buildSCC() {
|
|
704
656
|
const graph = this.buildNeuronGraph();
|
|
705
|
-
const neuronIds = this.neurons.map((n) => n.name);
|
|
706
657
|
const index = /* @__PURE__ */ new Map();
|
|
707
658
|
const lowlink = /* @__PURE__ */ new Map();
|
|
708
659
|
const onStack = /* @__PURE__ */ new Set();
|
|
709
660
|
const stack = [];
|
|
710
661
|
const components = [];
|
|
711
662
|
let currentIndex = 0;
|
|
712
|
-
const strongConnect = (
|
|
713
|
-
index.set(
|
|
714
|
-
lowlink.set(
|
|
663
|
+
const strongConnect = (neuron2) => {
|
|
664
|
+
index.set(neuron2, currentIndex);
|
|
665
|
+
lowlink.set(neuron2, currentIndex);
|
|
715
666
|
currentIndex++;
|
|
716
|
-
stack.push(
|
|
717
|
-
onStack.add(
|
|
718
|
-
const neighbors = graph.get(
|
|
667
|
+
stack.push(neuron2);
|
|
668
|
+
onStack.add(neuron2);
|
|
669
|
+
const neighbors = graph.get(neuron2) || /* @__PURE__ */ new Set();
|
|
719
670
|
for (const neighbor of Array.from(neighbors)) {
|
|
720
671
|
if (!index.has(neighbor)) {
|
|
721
672
|
strongConnect(neighbor);
|
|
722
673
|
lowlink.set(
|
|
723
|
-
|
|
724
|
-
Math.min(lowlink.get(
|
|
674
|
+
neuron2,
|
|
675
|
+
Math.min(lowlink.get(neuron2), lowlink.get(neighbor))
|
|
725
676
|
);
|
|
726
677
|
} else if (onStack.has(neighbor)) {
|
|
727
678
|
lowlink.set(
|
|
728
|
-
|
|
729
|
-
Math.min(lowlink.get(
|
|
679
|
+
neuron2,
|
|
680
|
+
Math.min(lowlink.get(neuron2), index.get(neighbor))
|
|
730
681
|
);
|
|
731
682
|
}
|
|
732
683
|
}
|
|
733
|
-
if (lowlink.get(
|
|
684
|
+
if (lowlink.get(neuron2) === index.get(neuron2)) {
|
|
734
685
|
const component = /* @__PURE__ */ new Set();
|
|
735
686
|
let w;
|
|
736
687
|
do {
|
|
737
688
|
w = stack.pop();
|
|
738
689
|
onStack.delete(w);
|
|
739
690
|
component.add(w);
|
|
740
|
-
} while (w !==
|
|
691
|
+
} while (w !== neuron2);
|
|
741
692
|
components.push(component);
|
|
742
693
|
}
|
|
743
694
|
};
|
|
744
|
-
for (const
|
|
745
|
-
if (!index.has(
|
|
746
|
-
strongConnect(
|
|
695
|
+
for (const neuron2 of this.neurons) {
|
|
696
|
+
if (!index.has(neuron2)) {
|
|
697
|
+
strongConnect(neuron2);
|
|
747
698
|
}
|
|
748
699
|
}
|
|
749
700
|
this.stronglyConnectedComponents = components;
|
|
@@ -765,10 +716,10 @@ var CNSNetwork = class {
|
|
|
765
716
|
}
|
|
766
717
|
for (let i = 0; i < this.stronglyConnectedComponents.length; i++) {
|
|
767
718
|
const scc = this.stronglyConnectedComponents[i];
|
|
768
|
-
for (const
|
|
769
|
-
const neighbors = neuronGraph.get(
|
|
770
|
-
for (const
|
|
771
|
-
const neighborSccIndex = this.neuronToSCC.get(
|
|
719
|
+
for (const neuron2 of Array.from(scc)) {
|
|
720
|
+
const neighbors = neuronGraph.get(neuron2) || /* @__PURE__ */ new Set();
|
|
721
|
+
for (const neighbor of Array.from(neighbors)) {
|
|
722
|
+
const neighborSccIndex = this.neuronToSCC.get(neighbor);
|
|
772
723
|
if (neighborSccIndex !== void 0 && neighborSccIndex !== i) {
|
|
773
724
|
this.sccDag.get(neighborSccIndex).add(i);
|
|
774
725
|
}
|
|
@@ -829,23 +780,23 @@ var CNSNetwork = class {
|
|
|
829
780
|
* Returns true if the neuron is in a strongly connected component with more than one member,
|
|
830
781
|
* or if it's in a single-member SCC that can reach itself.
|
|
831
782
|
*/
|
|
832
|
-
|
|
833
|
-
const sccIndex = this.neuronToSCC.get(
|
|
783
|
+
getSCCSetByNeuron(neuron2) {
|
|
784
|
+
const sccIndex = this.neuronToSCC.get(neuron2);
|
|
834
785
|
if (sccIndex === void 0) return;
|
|
835
786
|
return this.stronglyConnectedComponents[sccIndex];
|
|
836
787
|
}
|
|
837
788
|
/**
|
|
838
|
-
* Get the SCC index for a given neuron
|
|
789
|
+
* Get the SCC index for a given neuron
|
|
839
790
|
*/
|
|
840
|
-
|
|
841
|
-
return this.neuronToSCC.get(
|
|
791
|
+
getSccIndexByNeuron(neuron2) {
|
|
792
|
+
return this.neuronToSCC.get(neuron2);
|
|
842
793
|
}
|
|
843
794
|
/**
|
|
844
795
|
* Check if a neuron can be guaranteed not to be visited again during the current propagation.
|
|
845
796
|
* This is the core logic for safe context cleanup.
|
|
846
797
|
*/
|
|
847
|
-
canNeuronBeGuaranteedDone(
|
|
848
|
-
const sccIndex = this.neuronToSCC.get(
|
|
798
|
+
canNeuronBeGuaranteedDone(neuron2, activeSccCounts) {
|
|
799
|
+
const sccIndex = this.neuronToSCC.get(neuron2);
|
|
849
800
|
if (sccIndex === void 0) return true;
|
|
850
801
|
if (activeSccCounts.get(sccIndex) && activeSccCounts.get(sccIndex) > 0) {
|
|
851
802
|
return false;
|
|
@@ -861,11 +812,12 @@ var CNSNetwork = class {
|
|
|
861
812
|
}
|
|
862
813
|
buildIndexes() {
|
|
863
814
|
this.subIndex.clear();
|
|
864
|
-
this.
|
|
815
|
+
this.parentNeuronByCollateral.clear();
|
|
816
|
+
this.dendriteIndex.clear();
|
|
817
|
+
this.collateralIndex.clear();
|
|
865
818
|
for (const neuron2 of this.neurons) {
|
|
866
|
-
this.neuronIndex.set(neuron2.name, neuron2);
|
|
867
819
|
for (const dendrite of neuron2.dendrites) {
|
|
868
|
-
const key = dendrite.collateral
|
|
820
|
+
const key = dendrite.collateral;
|
|
869
821
|
const arr = this.subIndex.get(key) ?? [];
|
|
870
822
|
arr.push({ neuron: neuron2, dendrite });
|
|
871
823
|
this.subIndex.set(
|
|
@@ -875,19 +827,16 @@ var CNSNetwork = class {
|
|
|
875
827
|
this.dendriteIndex.set(key, dendrite);
|
|
876
828
|
}
|
|
877
829
|
Object.values(neuron2.axon).forEach((collateral2) => {
|
|
878
|
-
this.
|
|
879
|
-
collateral2
|
|
830
|
+
this.parentNeuronByCollateral.set(
|
|
831
|
+
collateral2,
|
|
880
832
|
neuron2
|
|
881
833
|
);
|
|
882
|
-
this.collateralIndex.
|
|
883
|
-
collateral2.name,
|
|
884
|
-
collateral2
|
|
885
|
-
);
|
|
834
|
+
this.collateralIndex.add(collateral2);
|
|
886
835
|
});
|
|
887
836
|
}
|
|
888
837
|
}
|
|
889
|
-
|
|
890
|
-
return this.
|
|
838
|
+
getParentNeuronByCollateral(collateral2) {
|
|
839
|
+
return this.parentNeuronByCollateral.get(collateral2);
|
|
891
840
|
}
|
|
892
841
|
getDendrites() {
|
|
893
842
|
return Array.from(this.dendriteIndex.values());
|
|
@@ -895,19 +844,8 @@ var CNSNetwork = class {
|
|
|
895
844
|
getCollaterals() {
|
|
896
845
|
return Array.from(this.collateralIndex.values());
|
|
897
846
|
}
|
|
898
|
-
|
|
899
|
-
return
|
|
900
|
-
}
|
|
901
|
-
getCollateralByName(collateralName) {
|
|
902
|
-
return this.collateralIndex.get(
|
|
903
|
-
collateralName
|
|
904
|
-
);
|
|
905
|
-
}
|
|
906
|
-
getNeuronByName(neuronName) {
|
|
907
|
-
return this.neuronIndex.get(neuronName);
|
|
908
|
-
}
|
|
909
|
-
getSubscribers(collateralName) {
|
|
910
|
-
return this.subIndex.get(collateralName) ?? [];
|
|
847
|
+
getSubscribers(collateral2) {
|
|
848
|
+
return this.subIndex.get(collateral2) ?? [];
|
|
911
849
|
}
|
|
912
850
|
};
|
|
913
851
|
|
|
@@ -916,10 +854,6 @@ var CNS = class {
|
|
|
916
854
|
constructor(neurons, options) {
|
|
917
855
|
this.neurons = neurons;
|
|
918
856
|
this.options = options;
|
|
919
|
-
/**
|
|
920
|
-
* Global per-neuron concurrency gates shared across all stimulations.
|
|
921
|
-
*/
|
|
922
|
-
this.neuronGates = /* @__PURE__ */ new Map();
|
|
923
857
|
/**
|
|
924
858
|
* Global task queue used by stimulation to schedule per-neuron gated work.
|
|
925
859
|
* Does not replace per-stimulation concurrency; only coordinates global per-neuron limits.
|
|
@@ -985,10 +919,14 @@ var CNS = class {
|
|
|
985
919
|
}
|
|
986
920
|
stimulate(signalOrSignals, options) {
|
|
987
921
|
const wrapped = this.wrapOnResponse(options?.onResponse);
|
|
988
|
-
const stimulation = new CNSStimulation(
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
922
|
+
const stimulation = new CNSStimulation(
|
|
923
|
+
this,
|
|
924
|
+
this.instanceNeuronQueue,
|
|
925
|
+
{
|
|
926
|
+
...options,
|
|
927
|
+
onResponse: wrapped
|
|
928
|
+
}
|
|
929
|
+
);
|
|
992
930
|
stimulation.responseToSignal(signalOrSignals);
|
|
993
931
|
return stimulation;
|
|
994
932
|
}
|
|
@@ -997,10 +935,14 @@ var CNS = class {
|
|
|
997
935
|
*/
|
|
998
936
|
activate(tasks, options) {
|
|
999
937
|
const wrapped = this.wrapOnResponse(options?.onResponse);
|
|
1000
|
-
const stimulation = new CNSStimulation(
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
938
|
+
const stimulation = new CNSStimulation(
|
|
939
|
+
this,
|
|
940
|
+
this.instanceNeuronQueue,
|
|
941
|
+
{
|
|
942
|
+
...options,
|
|
943
|
+
onResponse: wrapped
|
|
944
|
+
}
|
|
945
|
+
);
|
|
1004
946
|
stimulation.enqueueTasks(tasks);
|
|
1005
947
|
return stimulation;
|
|
1006
948
|
}
|
|
@@ -1008,17 +950,14 @@ var CNS = class {
|
|
|
1008
950
|
|
|
1009
951
|
// src/CNSCollateral.ts
|
|
1010
952
|
var CNSCollateral = class {
|
|
1011
|
-
constructor(name) {
|
|
1012
|
-
this.name = name;
|
|
1013
|
-
}
|
|
1014
953
|
createSignal(payload) {
|
|
1015
|
-
return {
|
|
954
|
+
return { collateral: this, payload };
|
|
1016
955
|
}
|
|
1017
956
|
};
|
|
1018
957
|
|
|
1019
958
|
// src/factory/index.ts
|
|
1020
|
-
var collateral = (
|
|
1021
|
-
var neuron = (
|
|
959
|
+
var collateral = () => new CNSCollateral();
|
|
960
|
+
var neuron = (axon) => {
|
|
1022
961
|
const dendrites = [];
|
|
1023
962
|
const api = {
|
|
1024
963
|
setConcurrency: (n) => {
|
|
@@ -1043,25 +982,122 @@ var neuron = (name, axon) => {
|
|
|
1043
982
|
return api;
|
|
1044
983
|
}),
|
|
1045
984
|
axon,
|
|
1046
|
-
name,
|
|
1047
985
|
concurrency: void 0,
|
|
1048
986
|
maxDuration: void 0,
|
|
1049
987
|
dendrites,
|
|
1050
988
|
dendrite(s) {
|
|
1051
|
-
|
|
1052
|
-
s
|
|
1053
|
-
|
|
989
|
+
if ("collateral" in s && "response" in s && Array.isArray(s.collateral)) {
|
|
990
|
+
for (const collateral2 of s.collateral) {
|
|
991
|
+
dendrites.push({
|
|
992
|
+
collateral: collateral2,
|
|
993
|
+
response: s.response
|
|
994
|
+
});
|
|
995
|
+
}
|
|
996
|
+
} else {
|
|
997
|
+
dendrites.push(
|
|
998
|
+
s
|
|
999
|
+
);
|
|
1000
|
+
}
|
|
1001
|
+
return api;
|
|
1002
|
+
},
|
|
1003
|
+
modalityDendrite(s) {
|
|
1004
|
+
const {
|
|
1005
|
+
collateral: collateral2,
|
|
1006
|
+
modality: singleModality,
|
|
1007
|
+
modalities: multipleModalities,
|
|
1008
|
+
afferentPaths: singleAfferentPaths,
|
|
1009
|
+
default: singleDefaultHandler,
|
|
1010
|
+
default: globalDefaultHandler,
|
|
1011
|
+
output
|
|
1012
|
+
} = s;
|
|
1013
|
+
const modalityConfigs = multipleModalities ? multipleModalities : singleModality ? [
|
|
1014
|
+
{
|
|
1015
|
+
modality: singleModality,
|
|
1016
|
+
afferentPaths: singleAfferentPaths,
|
|
1017
|
+
default: singleDefaultHandler
|
|
1018
|
+
}
|
|
1019
|
+
] : [];
|
|
1020
|
+
const response = (payload, axon2, ctx) => {
|
|
1021
|
+
const stimulation = ctx.stimulation;
|
|
1022
|
+
const stimOptions = stimulation?.options;
|
|
1023
|
+
const runGlobalDefault = () => {
|
|
1024
|
+
if (!globalDefaultHandler) {
|
|
1025
|
+
throw new Error(
|
|
1026
|
+
`modalityDendrite: No handler found for modality and no default handler provided`
|
|
1027
|
+
);
|
|
1028
|
+
}
|
|
1029
|
+
const res = globalDefaultHandler(payload, axon2, ctx);
|
|
1030
|
+
if (res && typeof res.then === "function") {
|
|
1031
|
+
return res.then(
|
|
1032
|
+
(result) => output(result, axon2, ctx)
|
|
1033
|
+
);
|
|
1034
|
+
}
|
|
1035
|
+
return output(res, axon2, ctx);
|
|
1036
|
+
};
|
|
1037
|
+
if (!stimOptions || !stimOptions.modality) {
|
|
1038
|
+
return runGlobalDefault();
|
|
1039
|
+
}
|
|
1040
|
+
const matchingConfig = modalityConfigs.find(
|
|
1041
|
+
(config) => config.modality === stimOptions.modality
|
|
1042
|
+
);
|
|
1043
|
+
if (!matchingConfig) {
|
|
1044
|
+
return runGlobalDefault();
|
|
1045
|
+
}
|
|
1046
|
+
const stimAfferentPath = stimOptions.afferentPath;
|
|
1047
|
+
const handler = stimAfferentPath && matchingConfig.afferentPaths ? matchingConfig.afferentPaths.get(stimAfferentPath) : void 0;
|
|
1048
|
+
const effectiveHandler = handler ?? matchingConfig.default ?? globalDefaultHandler;
|
|
1049
|
+
if (!effectiveHandler) {
|
|
1050
|
+
throw new Error(
|
|
1051
|
+
`modalityDendrite: No handler found for afferent path in modality and no default handler provided`
|
|
1052
|
+
);
|
|
1053
|
+
}
|
|
1054
|
+
const maybeResult = effectiveHandler(payload, axon2, ctx);
|
|
1055
|
+
if (maybeResult && typeof maybeResult.then === "function") {
|
|
1056
|
+
return maybeResult.then(
|
|
1057
|
+
(result) => output(result, axon2, ctx)
|
|
1058
|
+
);
|
|
1059
|
+
}
|
|
1060
|
+
try {
|
|
1061
|
+
return output(maybeResult, axon2, ctx);
|
|
1062
|
+
} catch (error) {
|
|
1063
|
+
if (error instanceof Error && error.message.includes(
|
|
1064
|
+
"Cannot read properties of undefined"
|
|
1065
|
+
)) {
|
|
1066
|
+
const axonKeys = axon2 && typeof axon2 === "object" ? Object.keys(axon2) : "not an object";
|
|
1067
|
+
throw new Error(
|
|
1068
|
+
`modalityDendrite: axon.output is undefined. Axon type: ${typeof axon2}, Axon keys: ${Array.isArray(axonKeys) ? axonKeys.join(", ") : axonKeys}. Make sure the neuron was created with the correct axon structure. Original error: ${error.message}`
|
|
1069
|
+
);
|
|
1070
|
+
}
|
|
1071
|
+
throw error;
|
|
1072
|
+
}
|
|
1073
|
+
};
|
|
1074
|
+
dendrites.push({
|
|
1075
|
+
collateral: collateral2,
|
|
1076
|
+
response
|
|
1077
|
+
});
|
|
1054
1078
|
return api;
|
|
1055
1079
|
}
|
|
1056
1080
|
};
|
|
1057
1081
|
return api;
|
|
1058
1082
|
};
|
|
1059
1083
|
var withCtx = () => ({
|
|
1060
|
-
neuron: (
|
|
1061
|
-
return neuron(
|
|
1084
|
+
neuron: (axon) => {
|
|
1085
|
+
return neuron(axon);
|
|
1062
1086
|
}
|
|
1063
1087
|
});
|
|
1088
|
+
var afferentPath = (parentAfferentPath) => {
|
|
1089
|
+
return {
|
|
1090
|
+
...parentAfferentPath !== void 0 && {
|
|
1091
|
+
parentAfferentPath
|
|
1092
|
+
}
|
|
1093
|
+
};
|
|
1094
|
+
};
|
|
1095
|
+
var modality = (afferentPaths) => {
|
|
1096
|
+
return {
|
|
1097
|
+
afferentPaths
|
|
1098
|
+
};
|
|
1099
|
+
};
|
|
1064
1100
|
|
|
1065
|
-
export { CNS, CNSCollateral, CNSStimulationContextStore, collateral, neuron, withCtx };
|
|
1101
|
+
export { CNS, CNSCollateral, CNSStimulationContextStore, afferentPath, collateral, modality, neuron, withCtx };
|
|
1066
1102
|
//# sourceMappingURL=index.js.map
|
|
1067
1103
|
//# sourceMappingURL=index.js.map
|