@aleph-ai/tinyaleph 1.5.5 → 1.5.7

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.
@@ -0,0 +1,1465 @@
1
+
2
+ /**
3
+ * Holographic Quantum Encoding (HQE)
4
+ *
5
+ * Implements the holographic projection mechanism from "A Design for a
6
+ * Sentient Observer" paper, Section 5.
7
+ *
8
+ * Key features:
9
+ * - Discrete Fourier Transform holographic projection
10
+ * - Spatial interference patterns from prime states
11
+ * - Pattern reconstruction via inverse DFT
12
+ * - Similarity metrics between holographic patterns
13
+ * - Distributed, non-local semantic representation
14
+ * - Dynamic λ(t) stabilization control (equation 12)
15
+ * - CRT-Homology integration for consistency detection
16
+ *
17
+ * Browser-compatible: No Node.js-specific dependencies
18
+ *
19
+ * @module observer/hqe
20
+ */
21
+
22
+ import { Complex, PrimeState } from '../core/hilbert.js';
23
+ import { firstNPrimes } from '../core/prime.js';
24
+
25
+ // CRT-Homology components for consistency detection
26
+ import { CRTReconstructor, HomologyLoss, CoprimeSelector, ResidueEncoder, BirkhoffProjector } from '../core/crt-homology.js';
27
+
28
+ // ════════════════════════════════════════════════════════════════════
29
+ // TICK GATE (from discrete.pdf Section 4.2)
30
+ // Gates expensive holographic operations to tick events only
31
+ // ════════════════════════════════════════════════════════════════════
32
+
33
+ /**
34
+ * Tick Gate for HQE operations
35
+ *
36
+ * From discrete.pdf: HQE computation should only occur on valid tick events
37
+ * to prevent continuous CPU usage and maintain discrete-time semantics.
38
+ *
39
+ * Tick conditions:
40
+ * 1. Minimum time elapsed since last tick
41
+ * 2. Coherence threshold crossed
42
+ * 3. External event trigger
43
+ */
44
+ class TickGate {
45
+ /**
46
+ * Create a tick gate
47
+ * @param {Object} options - Configuration
48
+ * @param {number} [options.minTickInterval=16] - Minimum ms between ticks (~60fps max)
49
+ * @param {number} [options.coherenceThreshold=0.7] - Coherence threshold for auto-tick
50
+ * @param {number} [options.maxTickHistory=50] - Maximum tick history size
51
+ * @param {'strict'|'adaptive'|'free'} [options.mode='adaptive'] - Gating mode
52
+ */
53
+ constructor(options = {}) {
54
+ // Minimum milliseconds between ticks
55
+ this.minTickInterval = options.minTickInterval || 16; // ~60fps max
56
+
57
+ // Coherence threshold for triggering tick
58
+ this.coherenceThreshold = options.coherenceThreshold || 0.7;
59
+
60
+ // Last tick timestamp
61
+ this.lastTickTime = 0;
62
+
63
+ // Tick counter
64
+ this.tickCount = 0;
65
+
66
+ // Pending tick flag (set by external events)
67
+ this.pendingTick = false;
68
+
69
+ // Tick history for analysis
70
+ this.tickHistory = [];
71
+ this.maxTickHistory = options.maxTickHistory || 50;
72
+
73
+ // Gating mode
74
+ // 'strict' - only process on explicit ticks
75
+ // 'adaptive' - allow through if coherence is high
76
+ // 'free' - no gating (legacy behavior)
77
+ this.mode = options.mode || 'adaptive';
78
+
79
+ // Statistics
80
+ this.gatedCount = 0; // Number of operations gated (blocked)
81
+ this.passedCount = 0; // Number of operations passed
82
+ }
83
+
84
+ /**
85
+ * Register an external tick event
86
+ * Called by PRSC or SMF when coherence spikes
87
+ */
88
+ tick() {
89
+ const now = Date.now();
90
+ this.pendingTick = true;
91
+ this.lastTickTime = now;
92
+ this.tickCount++;
93
+
94
+ this.tickHistory.push({
95
+ time: now,
96
+ count: this.tickCount
97
+ });
98
+
99
+ if (this.tickHistory.length > this.maxTickHistory) {
100
+ this.tickHistory.shift();
101
+ }
102
+ }
103
+
104
+ /**
105
+ * Check if an operation should proceed (tick gate)
106
+ *
107
+ * @param {Object} state - Current system state
108
+ * @param {number} [state.coherence=0] - Current coherence level
109
+ * @returns {Object} Gate result
110
+ */
111
+ shouldProcess(state = {}) {
112
+ const now = Date.now();
113
+ const timeSinceLastTick = now - this.lastTickTime;
114
+ const coherence = state.coherence || 0;
115
+
116
+ let shouldPass = false;
117
+ let reason = '';
118
+
119
+ switch (this.mode) {
120
+ case 'free':
121
+ // No gating - always pass
122
+ shouldPass = true;
123
+ reason = 'free_mode';
124
+ break;
125
+
126
+ case 'strict':
127
+ // Only pass on explicit pending tick
128
+ shouldPass = this.pendingTick;
129
+ reason = shouldPass ? 'pending_tick' : 'no_tick';
130
+ break;
131
+
132
+ case 'adaptive':
133
+ default:
134
+ // Pass if:
135
+ // 1. Pending tick exists, OR
136
+ // 2. Coherence exceeds threshold AND minimum interval elapsed
137
+ if (this.pendingTick) {
138
+ shouldPass = true;
139
+ reason = 'pending_tick';
140
+ } else if (coherence >= this.coherenceThreshold &&
141
+ timeSinceLastTick >= this.minTickInterval) {
142
+ shouldPass = true;
143
+ reason = 'coherence_threshold';
144
+ // Auto-register this as a tick
145
+ this.tick();
146
+ } else if (timeSinceLastTick >= this.minTickInterval * 10) {
147
+ // Fallback: allow through if way too long since last tick
148
+ shouldPass = true;
149
+ reason = 'timeout_fallback';
150
+ this.tick();
151
+ } else {
152
+ reason = 'gated';
153
+ }
154
+ break;
155
+ }
156
+
157
+ // Clear pending tick if we're processing
158
+ if (shouldPass) {
159
+ this.pendingTick = false;
160
+ this.passedCount++;
161
+ } else {
162
+ this.gatedCount++;
163
+ }
164
+
165
+ return {
166
+ shouldPass,
167
+ reason,
168
+ tickCount: this.tickCount,
169
+ timeSinceLastTick,
170
+ coherence,
171
+ mode: this.mode
172
+ };
173
+ }
174
+
175
+ /**
176
+ * Get tick rate (ticks per second)
177
+ * @returns {number} Ticks per second
178
+ */
179
+ getTickRate() {
180
+ if (this.tickHistory.length < 2) return 0;
181
+
182
+ const recent = this.tickHistory.slice(-10);
183
+ const duration = recent[recent.length - 1].time - recent[0].time;
184
+
185
+ if (duration <= 0) return 0;
186
+ return ((recent.length - 1) / duration) * 1000;
187
+ }
188
+
189
+ /**
190
+ * Get gating statistics
191
+ * @returns {Object} Statistics
192
+ */
193
+ getStats() {
194
+ const total = this.passedCount + this.gatedCount;
195
+ return {
196
+ tickCount: this.tickCount,
197
+ tickRate: this.getTickRate(),
198
+ passedCount: this.passedCount,
199
+ gatedCount: this.gatedCount,
200
+ gateRatio: total > 0 ? this.gatedCount / total : 0,
201
+ mode: this.mode,
202
+ lastTickTime: this.lastTickTime
203
+ };
204
+ }
205
+
206
+ /**
207
+ * Reset gate state
208
+ */
209
+ reset() {
210
+ this.tickCount = 0;
211
+ this.lastTickTime = 0;
212
+ this.pendingTick = false;
213
+ this.tickHistory = [];
214
+ this.gatedCount = 0;
215
+ this.passedCount = 0;
216
+ }
217
+
218
+ /**
219
+ * Set gating mode
220
+ * @param {'strict'|'adaptive'|'free'} mode - New mode
221
+ */
222
+ setMode(mode) {
223
+ if (['strict', 'adaptive', 'free'].includes(mode)) {
224
+ this.mode = mode;
225
+ }
226
+ }
227
+ }
228
+
229
+ // ════════════════════════════════════════════════════════════════════
230
+ // STABILIZATION CONTROLLER (equation 12) with CRT-Homology Integration
231
+ // ════════════════════════════════════════════════════════════════════
232
+
233
+ /**
234
+ * Stabilization Controller with Homology-Aware Dynamics
235
+ *
236
+ * Implements dynamic λ(t) from equation 12, enhanced with CRT-Homology:
237
+ * λ(t) = λ₀ · σ(aC·C(t) - aS·S(t) - aSMF·SSMF(s(t)) + aH·H(t))
238
+ *
239
+ * Where H(t) = homology penalty from detected semantic holes.
240
+ *
241
+ * Key insight: "Holes are consistency failures that persist under perturbation"
242
+ * - Ker(ℛ) detection identifies CRT reconstruction failures
243
+ * - Betti numbers (β₀, β₁) serve as topological invariants
244
+ * - Hole persistence correlates with Lyapunov instability (λ > 0)
245
+ *
246
+ * Controls the "condensation pressure" - balance between
247
+ * unitary evolution and dissipative stabilization.
248
+ */
249
+ class StabilizationController {
250
+ /**
251
+ * Create a stabilization controller
252
+ * @param {Object} options - Configuration
253
+ * @param {number} [options.lambda0=0.1] - Base stabilization rate λ₀
254
+ * @param {number} [options.aC=1.0] - Coherence weight
255
+ * @param {number} [options.aS=0.8] - Entropy weight
256
+ * @param {number} [options.aSMF=0.5] - SMF entropy weight
257
+ * @param {number} [options.aH=0.3] - Homology weight (new)
258
+ * @param {number} [options.steepness=2.0] - Sigmoid steepness
259
+ * @param {number} [options.lambdaMin=0.01] - Minimum λ
260
+ * @param {number} [options.lambdaMax=0.5] - Maximum λ
261
+ * @param {number} [options.maxHistory=100] - History size
262
+ * @param {Object} [options.homology] - Homology detection options
263
+ */
264
+ constructor(options = {}) {
265
+ // Base stabilization rate λ₀
266
+ this.lambda0 = options.lambda0 || 0.1;
267
+
268
+ // Weighting coefficients
269
+ this.aC = options.aC || 1.0; // Coherence weight (positive: high C increases λ)
270
+ this.aS = options.aS || 0.8; // Entropy weight (positive: high S decreases λ)
271
+ this.aSMF = options.aSMF || 0.5; // SMF entropy weight
272
+ this.aH = options.aH || 0.3; // Homology weight (new: holes increase λ)
273
+
274
+ // Sigmoid steepness
275
+ this.steepness = options.steepness || 2.0;
276
+
277
+ // Bounds for λ
278
+ this.lambdaMin = options.lambdaMin || 0.01;
279
+ this.lambdaMax = options.lambdaMax || 0.5;
280
+
281
+ // History for analysis
282
+ this.history = [];
283
+ this.maxHistory = options.maxHistory || 100;
284
+
285
+ // CRT-Homology integration
286
+ const homologyOpts = options.homology || {};
287
+ this.homologyEnabled = homologyOpts.enabled !== false;
288
+
289
+ if (this.homologyEnabled) {
290
+ // Select coprime moduli for CRT
291
+ const selector = new CoprimeSelector(homologyOpts.numModuli || 4);
292
+ this.moduli = selector.selectMinimal();
293
+
294
+ // Initialize CRT components
295
+ this.crt = new CRTReconstructor(this.moduli);
296
+ this.homologyLoss = new HomologyLoss({
297
+ tau: homologyOpts.tau || 0.1,
298
+ alpha: homologyOpts.alpha || 0.5,
299
+ beta: homologyOpts.beta || 1.0,
300
+ gamma: homologyOpts.gamma || 0.5
301
+ });
302
+
303
+ // Residue encoder (hidden dimension = moduli product for now)
304
+ const hiddenDim = homologyOpts.hiddenDim || 32;
305
+ this.residueEncoder = new ResidueEncoder(this.moduli, hiddenDim);
306
+
307
+ // Track homology state
308
+ this.homologyState = {
309
+ bettiNumbers: [1, 0], // [β₀, β₁]
310
+ holesDetected: 0,
311
+ lastError: 0,
312
+ cycleCount: 0
313
+ };
314
+ }
315
+ }
316
+
317
+ /**
318
+ * Sigmoid squashing function σ
319
+ * Maps (-∞, ∞) → (0, 1)
320
+ * @param {number} x - Input value
321
+ * @returns {number} Sigmoid output
322
+ */
323
+ sigmoid(x) {
324
+ return 1 / (1 + Math.exp(-this.steepness * x));
325
+ }
326
+
327
+ /**
328
+ * Compute homology penalty from residue vector
329
+ * H(t) = β₁ * (1 + log(1 + cycles)) * kernelError
330
+ *
331
+ * @param {Array<number>} residues - Expected residues from current state
332
+ * @returns {number} Homology penalty
333
+ */
334
+ computeHomologyPenalty(residues) {
335
+ if (!this.homologyEnabled || !residues || residues.length === 0) {
336
+ return 0;
337
+ }
338
+
339
+ try {
340
+ // Detect if current residues are in kernel (CRT failure)
341
+ const inKernel = this.crt.detectKernel(residues, this.homologyLoss.tau);
342
+ const error = this.crt.reconstructionError(residues);
343
+
344
+ // Update homology state
345
+ this.homologyState.lastError = error;
346
+
347
+ if (inKernel) {
348
+ this.homologyState.holesDetected++;
349
+
350
+ // Compute Betti numbers from residue batch (simplified)
351
+ // In full implementation, would track residue history
352
+ this.homologyState.bettiNumbers = [
353
+ 1, // β₀: always at least one component
354
+ Math.min(5, this.homologyState.holesDetected) // β₁: capped estimate
355
+ ];
356
+ }
357
+
358
+ const beta1 = this.homologyState.bettiNumbers[1];
359
+ const penalty = beta1 * (1 + Math.log(1 + this.homologyState.cycleCount)) * error;
360
+
361
+ return penalty;
362
+ } catch (e) {
363
+ // Fallback if homology computation fails
364
+ return 0;
365
+ }
366
+ }
367
+
368
+ /**
369
+ * Encode state to residues for homology analysis
370
+ * @param {Object} state - System state with prime activations
371
+ * @returns {Array<number>} Expected residues
372
+ */
373
+ stateToResidues(state) {
374
+ if (!this.homologyEnabled) return [];
375
+
376
+ // Convert state to hidden vector
377
+ const hiddenDim = this.residueEncoder.hiddenDim;
378
+ const h = new Float64Array(hiddenDim);
379
+
380
+ if (state.amplitudes) {
381
+ // Direct amplitude array
382
+ for (let i = 0; i < Math.min(state.amplitudes.length, hiddenDim); i++) {
383
+ h[i] = state.amplitudes[i];
384
+ }
385
+ } else if (state.primeActivations) {
386
+ // Prime -> activation map
387
+ let i = 0;
388
+ for (const [prime, value] of Object.entries(state.primeActivations)) {
389
+ if (i >= hiddenDim) break;
390
+ h[i++] = typeof value === 'number' ? value : (value.norm ? value.norm() : 0);
391
+ }
392
+ } else if (typeof state.coherence === 'number') {
393
+ // Use coherence and entropy as proxy
394
+ h[0] = state.coherence || 0;
395
+ h[1] = state.entropy || 0;
396
+ h[2] = state.smfEntropy || 0;
397
+ }
398
+
399
+ // Encode to residues
400
+ const residueDistributions = this.residueEncoder.encode(h);
401
+ return this.residueEncoder.expectedResidues(residueDistributions);
402
+ }
403
+
404
+ /**
405
+ * Compute current λ(t) value with homology awareness
406
+ *
407
+ * λ(t) = λ₀ · σ(aC·C(t) - aS·S(t) - aSMF·SSMF(s(t)) + aH·H(t))
408
+ *
409
+ * The homology term H(t) increases λ when semantic holes are detected,
410
+ * encouraging faster stabilization to resolve consistency failures.
411
+ *
412
+ * @param {number} coherence - Global coherence C(t) ∈ [0, 1]
413
+ * @param {number} entropy - System entropy S(t)
414
+ * @param {number} [smfEntropy=0] - SMF entropy SSMF
415
+ * @param {Object} [state=null] - Optional state for homology analysis
416
+ * @returns {number} Stabilization rate λ(t)
417
+ */
418
+ computeLambda(coherence, entropy, smfEntropy = 0, state = null) {
419
+ // Compute homology penalty if state provided
420
+ let homologyPenalty = 0;
421
+ if (this.homologyEnabled && state) {
422
+ const residues = this.stateToResidues(state);
423
+ homologyPenalty = this.computeHomologyPenalty(residues);
424
+ }
425
+
426
+ // Compute the argument to the sigmoid
427
+ // Note: homology penalty INCREASES λ (more stabilization when holes detected)
428
+ const arg = this.aC * coherence - this.aS * entropy - this.aSMF * smfEntropy + this.aH * homologyPenalty;
429
+
430
+ // Apply sigmoid and scale by λ₀
431
+ const lambda = this.lambda0 * this.sigmoid(arg);
432
+
433
+ // Clamp to bounds
434
+ const clampedLambda = Math.max(this.lambdaMin, Math.min(this.lambdaMax, lambda));
435
+
436
+ // Record to history
437
+ this.history.push({
438
+ timestamp: Date.now(),
439
+ coherence,
440
+ entropy,
441
+ smfEntropy,
442
+ homologyPenalty,
443
+ arg,
444
+ lambda: clampedLambda,
445
+ bettiNumbers: this.homologyEnabled ? [...this.homologyState.bettiNumbers] : null
446
+ });
447
+
448
+ if (this.history.length > this.maxHistory) {
449
+ this.history.shift();
450
+ }
451
+
452
+ return clampedLambda;
453
+ }
454
+
455
+ /**
456
+ * Get the interpretation of current λ
457
+ * @param {number} lambda - Current λ value
458
+ * @returns {string} Interpretation
459
+ */
460
+ interpret(lambda) {
461
+ if (lambda > 0.3) {
462
+ return 'high_stabilization'; // Strong condensation pressure
463
+ } else if (lambda > 0.1) {
464
+ return 'normal'; // Balanced
465
+ } else {
466
+ return 'low_stabilization'; // More unitary/exploratory
467
+ }
468
+ }
469
+
470
+ /**
471
+ * Get recent lambda trend
472
+ * @returns {number} Trend (positive = increasing, negative = decreasing)
473
+ */
474
+ getTrend() {
475
+ if (this.history.length < 5) return 0;
476
+
477
+ const recent = this.history.slice(-10);
478
+ const first = recent.slice(0, Math.floor(recent.length / 2));
479
+ const second = recent.slice(Math.floor(recent.length / 2));
480
+
481
+ const firstAvg = first.reduce((s, h) => s + h.lambda, 0) / first.length;
482
+ const secondAvg = second.reduce((s, h) => s + h.lambda, 0) / second.length;
483
+
484
+ return secondAvg - firstAvg;
485
+ }
486
+
487
+ /**
488
+ * Get statistics
489
+ * @returns {Object} Statistics
490
+ */
491
+ getStats() {
492
+ if (this.history.length === 0) {
493
+ return { current: this.lambda0, mean: this.lambda0, trend: 0 };
494
+ }
495
+
496
+ const lambdas = this.history.map(h => h.lambda);
497
+ const current = lambdas[lambdas.length - 1];
498
+ const mean = lambdas.reduce((a, b) => a + b, 0) / lambdas.length;
499
+
500
+ const stats = {
501
+ current,
502
+ mean,
503
+ min: Math.min(...lambdas),
504
+ max: Math.max(...lambdas),
505
+ trend: this.getTrend(),
506
+ interpretation: this.interpret(current)
507
+ };
508
+
509
+ // Add homology stats if enabled
510
+ if (this.homologyEnabled) {
511
+ stats.homology = {
512
+ enabled: true,
513
+ bettiNumbers: this.homologyState.bettiNumbers,
514
+ holesDetected: this.homologyState.holesDetected,
515
+ lastError: this.homologyState.lastError,
516
+ cycleCount: this.homologyState.cycleCount,
517
+ moduli: this.moduli
518
+ };
519
+ }
520
+
521
+ return stats;
522
+ }
523
+
524
+ /**
525
+ * Get homology state (topological invariants)
526
+ * @returns {Object} Homology state
527
+ */
528
+ getHomologyState() {
529
+ if (!this.homologyEnabled) {
530
+ return { enabled: false };
531
+ }
532
+
533
+ return {
534
+ enabled: true,
535
+ ...this.homologyState,
536
+ moduli: this.moduli,
537
+ P: this.crt.P // Product of moduli
538
+ };
539
+ }
540
+
541
+ /**
542
+ * Detect if system is in kernel (semantic hole)
543
+ * @param {Object} state - System state
544
+ * @returns {Object} Kernel detection result
545
+ */
546
+ detectKernel(state) {
547
+ if (!this.homologyEnabled) {
548
+ return { inKernel: false, error: 0 };
549
+ }
550
+
551
+ const residues = this.stateToResidues(state);
552
+ const inKernel = this.crt.detectKernel(residues, this.homologyLoss.tau);
553
+ const error = this.crt.reconstructionError(residues);
554
+
555
+ return {
556
+ inKernel,
557
+ error,
558
+ residues,
559
+ reconstructed: this.crt.reconstruct(residues)
560
+ };
561
+ }
562
+
563
+ /**
564
+ * Reset controller
565
+ */
566
+ reset() {
567
+ this.history = [];
568
+ if (this.homologyEnabled) {
569
+ this.homologyState = {
570
+ bettiNumbers: [1, 0],
571
+ holesDetected: 0,
572
+ lastError: 0,
573
+ cycleCount: 0
574
+ };
575
+ }
576
+ }
577
+
578
+ /**
579
+ * Serialize to JSON
580
+ * @returns {Object} JSON representation
581
+ */
582
+ toJSON() {
583
+ const json = {
584
+ config: {
585
+ lambda0: this.lambda0,
586
+ aC: this.aC,
587
+ aS: this.aS,
588
+ aSMF: this.aSMF,
589
+ aH: this.aH
590
+ },
591
+ stats: this.getStats()
592
+ };
593
+
594
+ if (this.homologyEnabled) {
595
+ json.homology = this.getHomologyState();
596
+ }
597
+
598
+ return json;
599
+ }
600
+ }
601
+
602
+ // ════════════════════════════════════════════════════════════════════
603
+ // HOLOGRAPHIC ENCODER (equations 13-15)
604
+ // ════════════════════════════════════════════════════════════════════
605
+
606
+ /**
607
+ * Holographic Quantum Encoding system
608
+ *
609
+ * Projects prime-amplitude states into spatial interference patterns
610
+ * using DFT, enabling distributed, reconstruction-capable memory.
611
+ */
612
+ class HolographicEncoder {
613
+ /**
614
+ * Create a holographic encoder
615
+ * @param {number} [gridSize=64] - Size of the 2D holographic grid
616
+ * @param {Array<number>|number} [primes=64] - Primes to use or count
617
+ * @param {Object} [options={}] - Configuration options
618
+ * @param {number} [options.wavelengthScale=10] - Wavelength scaling factor
619
+ * @param {number} [options.phaseOffset=0] - Global phase offset
620
+ * @param {Object} [options.stabilization] - StabilizationController options
621
+ * @param {Object} [options.tickGate] - TickGate options
622
+ */
623
+ constructor(gridSize = 64, primes = 64, options = {}) {
624
+ this.gridSize = gridSize;
625
+
626
+ // Handle primes argument
627
+ if (typeof primes === 'number') {
628
+ this.primes = firstNPrimes(primes);
629
+ } else if (Array.isArray(primes)) {
630
+ this.primes = primes;
631
+ } else {
632
+ this.primes = firstNPrimes(64);
633
+ }
634
+
635
+ this.primeToIndex = new Map(this.primes.map((p, i) => [p, i]));
636
+
637
+ // Configuration
638
+ this.wavelengthScale = options.wavelengthScale || 10;
639
+ this.phaseOffset = options.phaseOffset || 0;
640
+
641
+ // Stabilization controller for dynamic λ(t) (equation 12)
642
+ this.stabilization = new StabilizationController(options.stabilization || {});
643
+
644
+ // Tick gate for HQE operations (discrete.pdf Section 4.2)
645
+ this.tickGate = new TickGate(options.tickGate || {});
646
+
647
+ // Precompute spatial frequencies for each prime
648
+ this.spatialFrequencies = this.computeSpatialFrequencies();
649
+
650
+ // Holographic field (2D complex array)
651
+ this.field = this.createField();
652
+ }
653
+
654
+ /**
655
+ * Create an empty holographic field
656
+ * @returns {Array<Array<Complex>>} Empty 2D complex field
657
+ */
658
+ createField() {
659
+ const field = new Array(this.gridSize);
660
+ for (let i = 0; i < this.gridSize; i++) {
661
+ field[i] = new Array(this.gridSize);
662
+ for (let j = 0; j < this.gridSize; j++) {
663
+ field[i][j] = new Complex(0, 0);
664
+ }
665
+ }
666
+ return field;
667
+ }
668
+
669
+ /**
670
+ * Compute spatial frequencies for each prime
671
+ * Maps primes to (kx, ky) frequency pairs using golden ratio spiral
672
+ * @returns {Array<Object>} Frequency data for each prime
673
+ */
674
+ computeSpatialFrequencies() {
675
+ const phi = (1 + Math.sqrt(5)) / 2; // Golden ratio
676
+ const frequencies = [];
677
+
678
+ for (let i = 0; i < this.primes.length; i++) {
679
+ const p = this.primes[i];
680
+ // Use logarithmic prime mapping for wavelength
681
+ const wavelength = this.wavelengthScale * (1 + Math.log(p) / Math.log(2));
682
+ const k = 2 * Math.PI / wavelength;
683
+
684
+ // Distribute angles using golden ratio for optimal coverage
685
+ const angle = 2 * Math.PI * i * phi;
686
+
687
+ frequencies.push({
688
+ prime: p,
689
+ kx: k * Math.cos(angle),
690
+ ky: k * Math.sin(angle),
691
+ wavelength
692
+ });
693
+ }
694
+
695
+ return frequencies;
696
+ }
697
+
698
+ /**
699
+ * Project a prime state into the holographic field (equation 13)
700
+ * H(x,y,t) = Σp αp(t) exp(i[kp·r + φp(t)])
701
+ *
702
+ * @param {PrimeState|Object} state - Prime state or {prime: Complex} map
703
+ * @param {Object} [options={}] - Projection options
704
+ * @param {boolean} [options.clear=true] - Clear field before projection
705
+ * @returns {Array<Array<Complex>>} The holographic field
706
+ */
707
+ project(state, options = {}) {
708
+ const clear = options.clear !== false;
709
+
710
+ if (clear) {
711
+ this.clearField();
712
+ }
713
+
714
+ // Handle different state formats
715
+ let amplitudes;
716
+ if (state instanceof PrimeState) {
717
+ amplitudes = this.primes.map(p => state.get(p) || new Complex(0, 0));
718
+ } else if (typeof state === 'object') {
719
+ amplitudes = this.primes.map(p => {
720
+ const amp = state[p];
721
+ if (!amp) return new Complex(0, 0);
722
+ if (amp instanceof Complex) return amp;
723
+ if (typeof amp === 'number') return new Complex(amp, 0);
724
+ if (amp.re !== undefined) return new Complex(amp.re, amp.im || 0);
725
+ return new Complex(0, 0);
726
+ });
727
+ } else {
728
+ throw new Error('Invalid state format');
729
+ }
730
+
731
+ // Project each prime's contribution
732
+ for (let i = 0; i < this.primes.length; i++) {
733
+ const freq = this.spatialFrequencies[i];
734
+ const alpha = amplitudes[i];
735
+
736
+ if (alpha.norm() < 1e-10) continue;
737
+
738
+ // Add this prime's plane wave to the field
739
+ for (let x = 0; x < this.gridSize; x++) {
740
+ for (let y = 0; y < this.gridSize; y++) {
741
+ // k·r = kx*x + ky*y
742
+ const phase = freq.kx * x + freq.ky * y + this.phaseOffset;
743
+ const wave = Complex.fromPolar(1, phase);
744
+
745
+ // H(x,y) += αp * exp(i*k·r)
746
+ this.field[x][y] = this.field[x][y].add(alpha.mul(wave));
747
+ }
748
+ }
749
+ }
750
+
751
+ return this.field;
752
+ }
753
+
754
+ /**
755
+ * Reconstruct amplitudes from holographic field (equation 15)
756
+ * Uses inverse DFT to recover prime amplitudes
757
+ *
758
+ * @returns {Map<number, Complex>} Reconstructed amplitudes by prime
759
+ */
760
+ reconstruct() {
761
+ const amplitudes = new Map();
762
+
763
+ for (let i = 0; i < this.primes.length; i++) {
764
+ const freq = this.spatialFrequencies[i];
765
+ const prime = this.primes[i];
766
+
767
+ // Inverse DFT at this frequency
768
+ let sum = new Complex(0, 0);
769
+
770
+ for (let x = 0; x < this.gridSize; x++) {
771
+ for (let y = 0; y < this.gridSize; y++) {
772
+ // Inverse: exp(-i*k·r)
773
+ const phase = -(freq.kx * x + freq.ky * y + this.phaseOffset);
774
+ const wave = Complex.fromPolar(1, phase);
775
+
776
+ sum = sum.add(this.field[x][y].mul(wave));
777
+ }
778
+ }
779
+
780
+ // Normalize by grid size
781
+ sum = new Complex(
782
+ sum.re / (this.gridSize * this.gridSize),
783
+ sum.im / (this.gridSize * this.gridSize)
784
+ );
785
+
786
+ amplitudes.set(prime, sum);
787
+ }
788
+
789
+ return amplitudes;
790
+ }
791
+
792
+ /**
793
+ * Reconstruct to PrimeState
794
+ * @returns {PrimeState} Reconstructed prime state
795
+ */
796
+ reconstructToState() {
797
+ const amplitudes = this.reconstruct();
798
+ const state = new PrimeState(this.primes);
799
+
800
+ for (const [prime, amp] of amplitudes) {
801
+ state.set(prime, amp);
802
+ }
803
+
804
+ return state;
805
+ }
806
+
807
+ /**
808
+ * Compute intensity pattern (equation 14)
809
+ * I(x,y,t) = |H(x,y,t)|²
810
+ *
811
+ * @returns {Array<Array<number>>} Intensity at each grid point
812
+ */
813
+ intensity() {
814
+ const I = new Array(this.gridSize);
815
+ for (let x = 0; x < this.gridSize; x++) {
816
+ I[x] = new Array(this.gridSize);
817
+ for (let y = 0; y < this.gridSize; y++) {
818
+ const cell = this.field[x][y];
819
+ // Handle both Complex objects and plain {re, im} objects
820
+ if (typeof cell.normSq === 'function') {
821
+ I[x][y] = cell.normSq();
822
+ } else {
823
+ const re = cell.re !== undefined ? cell.re : 0;
824
+ const im = cell.im !== undefined ? cell.im : 0;
825
+ I[x][y] = re * re + im * im;
826
+ }
827
+ }
828
+ }
829
+ return I;
830
+ }
831
+
832
+ /**
833
+ * Compute real part pattern (for visualization)
834
+ * @returns {Array<Array<number>>} Real part at each grid point
835
+ */
836
+ realPart() {
837
+ const R = new Array(this.gridSize);
838
+ for (let x = 0; x < this.gridSize; x++) {
839
+ R[x] = new Array(this.gridSize);
840
+ for (let y = 0; y < this.gridSize; y++) {
841
+ R[x][y] = this.field[x][y].re;
842
+ }
843
+ }
844
+ return R;
845
+ }
846
+
847
+ /**
848
+ * Compute phase pattern (for visualization)
849
+ * @returns {Array<Array<number>>} Phase at each grid point
850
+ */
851
+ phasePattern() {
852
+ const P = new Array(this.gridSize);
853
+ for (let x = 0; x < this.gridSize; x++) {
854
+ P[x] = new Array(this.gridSize);
855
+ for (let y = 0; y < this.gridSize; y++) {
856
+ P[x][y] = this.field[x][y].phase();
857
+ }
858
+ }
859
+ return P;
860
+ }
861
+
862
+ /**
863
+ * Clear the holographic field
864
+ */
865
+ clearField() {
866
+ for (let x = 0; x < this.gridSize; x++) {
867
+ for (let y = 0; y < this.gridSize; y++) {
868
+ this.field[x][y] = new Complex(0, 0);
869
+ }
870
+ }
871
+ }
872
+
873
+ /**
874
+ * Add another field to this one (superposition)
875
+ * @param {Array<Array<Complex>>} otherField - Field to add
876
+ */
877
+ superpose(otherField) {
878
+ for (let x = 0; x < this.gridSize; x++) {
879
+ for (let y = 0; y < this.gridSize; y++) {
880
+ this.field[x][y] = this.field[x][y].add(otherField[x][y]);
881
+ }
882
+ }
883
+ }
884
+
885
+ /**
886
+ * Multiply field by a scalar
887
+ * @param {number} scalar - Multiplication factor
888
+ */
889
+ scale(scalar) {
890
+ for (let x = 0; x < this.gridSize; x++) {
891
+ for (let y = 0; y < this.gridSize; y++) {
892
+ this.field[x][y] = new Complex(
893
+ this.field[x][y].re * scalar,
894
+ this.field[x][y].im * scalar
895
+ );
896
+ }
897
+ }
898
+ }
899
+
900
+ /**
901
+ * Clone this encoder with its current field
902
+ * @returns {HolographicEncoder} Cloned encoder
903
+ */
904
+ clone() {
905
+ const cloned = new HolographicEncoder(this.gridSize, this.primes.slice(), {
906
+ wavelengthScale: this.wavelengthScale,
907
+ phaseOffset: this.phaseOffset
908
+ });
909
+
910
+ for (let x = 0; x < this.gridSize; x++) {
911
+ for (let y = 0; y < this.gridSize; y++) {
912
+ cloned.field[x][y] = new Complex(
913
+ this.field[x][y].re,
914
+ this.field[x][y].im
915
+ );
916
+ }
917
+ }
918
+
919
+ return cloned;
920
+ }
921
+
922
+ /**
923
+ * Compute total field energy
924
+ * @returns {number} Total energy
925
+ */
926
+ totalEnergy() {
927
+ let energy = 0;
928
+ for (let x = 0; x < this.gridSize; x++) {
929
+ for (let y = 0; y < this.gridSize; y++) {
930
+ const cell = this.field[x][y];
931
+ // Handle both Complex objects and plain {re, im} objects
932
+ if (typeof cell.normSq === 'function') {
933
+ energy += cell.normSq();
934
+ } else {
935
+ const re = cell.re !== undefined ? cell.re : 0;
936
+ const im = cell.im !== undefined ? cell.im : 0;
937
+ energy += re * re + im * im;
938
+ }
939
+ }
940
+ }
941
+ return energy;
942
+ }
943
+
944
+ /**
945
+ * Compute field entropy (based on intensity distribution)
946
+ * @returns {number} Field entropy in bits
947
+ */
948
+ fieldEntropy() {
949
+ const I = this.intensity();
950
+ const total = this.totalEnergy();
951
+
952
+ if (total < 1e-10) return 0;
953
+
954
+ let H = 0;
955
+ for (let x = 0; x < this.gridSize; x++) {
956
+ for (let y = 0; y < this.gridSize; y++) {
957
+ const p = I[x][y] / total;
958
+ if (p > 1e-10) {
959
+ H -= p * Math.log2(p);
960
+ }
961
+ }
962
+ }
963
+
964
+ return H;
965
+ }
966
+
967
+ /**
968
+ * Get state snapshot (compressed for storage)
969
+ * @returns {Object} Compressed state
970
+ */
971
+ getState() {
972
+ // Only store non-zero cells to save space
973
+ const cells = [];
974
+ for (let x = 0; x < this.gridSize; x++) {
975
+ for (let y = 0; y < this.gridSize; y++) {
976
+ const cell = this.field[x][y];
977
+ // Handle both Complex objects and plain {re, im} objects
978
+ const re = cell.re !== undefined ? cell.re : 0;
979
+ const im = cell.im !== undefined ? cell.im : 0;
980
+ const normSq = re * re + im * im;
981
+
982
+ if (normSq > 1e-10) {
983
+ cells.push({ x, y, re, im });
984
+ }
985
+ }
986
+ }
987
+
988
+ return {
989
+ gridSize: this.gridSize,
990
+ cells,
991
+ totalEnergy: this.totalEnergy()
992
+ };
993
+ }
994
+
995
+ /**
996
+ * Load state from snapshot
997
+ * @param {Object} state - State snapshot
998
+ */
999
+ loadState(state) {
1000
+ if (state.gridSize !== this.gridSize) {
1001
+ throw new Error('Grid size mismatch');
1002
+ }
1003
+
1004
+ this.clearField();
1005
+
1006
+ for (const cell of state.cells) {
1007
+ this.field[cell.x][cell.y] = new Complex(cell.re, cell.im);
1008
+ }
1009
+ }
1010
+
1011
+ /**
1012
+ * Evolve the holographic field with stabilization (equation 11)
1013
+ *
1014
+ * d|Ψ(t)⟩/dt = iĤ|Ψ(t)⟩ - λ(t)D̂(Ψ,s)|Ψ(t)⟩
1015
+ *
1016
+ * The first term is unitary (phase evolution), the second is dissipative
1017
+ * (stabilization toward coherent attractors).
1018
+ *
1019
+ * @param {Object} state - Current system state
1020
+ * @param {number} state.coherence - Global coherence C(t)
1021
+ * @param {number} state.entropy - System entropy S(t)
1022
+ * @param {number} [state.smfEntropy=0] - SMF entropy SSMF
1023
+ * @param {number} [dt=0.016] - Time step
1024
+ * @returns {Object} Evolution result with lambda value
1025
+ */
1026
+ evolve(state, dt = 0.016) {
1027
+ const { coherence, entropy, smfEntropy = 0 } = state;
1028
+
1029
+ // Check tick gate before expensive HQE operations
1030
+ const gateResult = this.tickGate.shouldProcess({ coherence });
1031
+
1032
+ if (!gateResult.shouldPass) {
1033
+ // Return gated result without processing
1034
+ return {
1035
+ lambda: 0,
1036
+ interpretation: 'gated',
1037
+ totalEnergy: this.totalEnergy(),
1038
+ fieldEntropy: this.fieldEntropy(),
1039
+ gated: true,
1040
+ gateReason: gateResult.reason,
1041
+ tickCount: gateResult.tickCount
1042
+ };
1043
+ }
1044
+
1045
+ // Compute dynamic λ(t) using stabilization controller
1046
+ const lambda = this.stabilization.computeLambda(coherence, entropy, smfEntropy);
1047
+
1048
+ // For each cell, apply damped evolution:
1049
+ // New amplitude = old amplitude * exp(-λ * dt)
1050
+ // This implements the dissipative term -λD̂|Ψ⟩
1051
+ const dampingFactor = Math.exp(-lambda * dt);
1052
+
1053
+ // Apply stabilization damping
1054
+ for (let x = 0; x < this.gridSize; x++) {
1055
+ for (let y = 0; y < this.gridSize; y++) {
1056
+ const cell = this.field[x][y];
1057
+ // Dampen high-energy cells more than low-energy (stabilization)
1058
+ const intensity = cell.re * cell.re + cell.im * cell.im;
1059
+ const localDamping = dampingFactor * (1 + lambda * intensity * 0.1);
1060
+
1061
+ this.field[x][y] = new Complex(
1062
+ cell.re * localDamping,
1063
+ cell.im * localDamping
1064
+ );
1065
+ }
1066
+ }
1067
+
1068
+ return {
1069
+ lambda,
1070
+ interpretation: this.stabilization.interpret(lambda),
1071
+ totalEnergy: this.totalEnergy(),
1072
+ fieldEntropy: this.fieldEntropy(),
1073
+ gated: false,
1074
+ tickCount: gateResult.tickCount
1075
+ };
1076
+ }
1077
+
1078
+ /**
1079
+ * Register a tick event (from PRSC or SMF coherence spike)
1080
+ */
1081
+ tick() {
1082
+ this.tickGate.tick();
1083
+ }
1084
+
1085
+ /**
1086
+ * Get tick gate statistics
1087
+ * @returns {Object} Statistics
1088
+ */
1089
+ getTickStats() {
1090
+ return this.tickGate.getStats();
1091
+ }
1092
+
1093
+ /**
1094
+ * Set tick gate mode
1095
+ * @param {'strict'|'adaptive'|'free'} mode - Gating mode
1096
+ */
1097
+ setTickMode(mode) {
1098
+ this.tickGate.setMode(mode);
1099
+ }
1100
+
1101
+ /**
1102
+ * Get stabilization statistics
1103
+ * @returns {Object} Statistics
1104
+ */
1105
+ getStabilizationStats() {
1106
+ return this.stabilization.getStats();
1107
+ }
1108
+ }
1109
+
1110
+ // ════════════════════════════════════════════════════════════════════
1111
+ // HOLOGRAPHIC MEMORY
1112
+ // ════════════════════════════════════════════════════════════════════
1113
+
1114
+ /**
1115
+ * Holographic Memory
1116
+ *
1117
+ * Stores and retrieves patterns using holographic interference.
1118
+ * Enables content-addressable, distributed, fault-tolerant memory.
1119
+ */
1120
+ class HolographicMemory {
1121
+ /**
1122
+ * Create a holographic memory
1123
+ * @param {number} [gridSize=64] - Size of holographic grid
1124
+ * @param {number|Array} [primes=64] - Primes to use
1125
+ * @param {Object} [options={}] - Configuration options
1126
+ * @param {number} [options.maxMemories=100] - Maximum memories
1127
+ * @param {number} [options.decayRate=0.01] - Memory decay rate
1128
+ */
1129
+ constructor(gridSize = 64, primes = 64, options = {}) {
1130
+ this.encoder = new HolographicEncoder(gridSize, primes, options);
1131
+ this.memories = [];
1132
+ this.maxMemories = options.maxMemories || 100;
1133
+ this.decayRate = options.decayRate || 0.01;
1134
+ }
1135
+
1136
+ /**
1137
+ * Store a pattern in memory
1138
+ * @param {PrimeState|Object} state - State to store
1139
+ * @param {Object} [metadata={}] - Associated metadata
1140
+ * @returns {number} Memory index
1141
+ */
1142
+ store(state, metadata = {}) {
1143
+ // Create a new encoder for this memory
1144
+ const encoder = new HolographicEncoder(
1145
+ this.encoder.gridSize,
1146
+ this.encoder.primes.slice(),
1147
+ {
1148
+ wavelengthScale: this.encoder.wavelengthScale,
1149
+ phaseOffset: this.encoder.phaseOffset
1150
+ }
1151
+ );
1152
+
1153
+ encoder.project(state);
1154
+
1155
+ this.memories.push({
1156
+ encoder,
1157
+ metadata,
1158
+ timestamp: Date.now(),
1159
+ accessCount: 0,
1160
+ strength: 1.0
1161
+ });
1162
+
1163
+ // Prune if over capacity
1164
+ if (this.memories.length > this.maxMemories) {
1165
+ this.prune();
1166
+ }
1167
+
1168
+ return this.memories.length - 1;
1169
+ }
1170
+
1171
+ /**
1172
+ * Recall the best matching memory
1173
+ * @param {PrimeState|Object} cue - Retrieval cue
1174
+ * @param {number} [threshold=0.3] - Minimum similarity threshold
1175
+ * @returns {Object|null} Best matching memory or null
1176
+ */
1177
+ recall(cue, threshold = 0.3) {
1178
+ if (this.memories.length === 0) return null;
1179
+
1180
+ // Project cue to holographic form
1181
+ const cueEncoder = new HolographicEncoder(
1182
+ this.encoder.gridSize,
1183
+ this.encoder.primes.slice()
1184
+ );
1185
+ cueEncoder.project(cue);
1186
+
1187
+ // Find best match by holographic correlation
1188
+ let bestMatch = null;
1189
+ let bestScore = threshold;
1190
+
1191
+ for (const memory of this.memories) {
1192
+ const score = this.correlate(cueEncoder, memory.encoder);
1193
+ if (score > bestScore) {
1194
+ bestScore = score;
1195
+ bestMatch = memory;
1196
+ }
1197
+ }
1198
+
1199
+ if (bestMatch) {
1200
+ bestMatch.accessCount++;
1201
+ bestMatch.strength = Math.min(1.0, bestMatch.strength + 0.1);
1202
+ }
1203
+
1204
+ return bestMatch ? {
1205
+ state: bestMatch.encoder.reconstructToState(),
1206
+ metadata: bestMatch.metadata,
1207
+ score: bestScore,
1208
+ strength: bestMatch.strength
1209
+ } : null;
1210
+ }
1211
+
1212
+ /**
1213
+ * Correlate two holographic fields
1214
+ * Returns normalized correlation coefficient
1215
+ *
1216
+ * @param {HolographicEncoder} enc1 - First encoder
1217
+ * @param {HolographicEncoder} enc2 - Second encoder
1218
+ * @returns {number} Correlation coefficient
1219
+ */
1220
+ correlate(enc1, enc2) {
1221
+ let sumProduct = 0;
1222
+ let sumSq1 = 0;
1223
+ let sumSq2 = 0;
1224
+
1225
+ for (let x = 0; x < enc1.gridSize; x++) {
1226
+ for (let y = 0; y < enc1.gridSize; y++) {
1227
+ // Compute normSq for each cell (handling both Complex and plain objects)
1228
+ const cell1 = enc1.field[x][y];
1229
+ const cell2 = enc2.field[x][y];
1230
+
1231
+ let v1, v2;
1232
+ if (typeof cell1.normSq === 'function') {
1233
+ v1 = cell1.normSq();
1234
+ } else {
1235
+ const re1 = cell1.re !== undefined ? cell1.re : 0;
1236
+ const im1 = cell1.im !== undefined ? cell1.im : 0;
1237
+ v1 = re1 * re1 + im1 * im1;
1238
+ }
1239
+
1240
+ if (typeof cell2.normSq === 'function') {
1241
+ v2 = cell2.normSq();
1242
+ } else {
1243
+ const re2 = cell2.re !== undefined ? cell2.re : 0;
1244
+ const im2 = cell2.im !== undefined ? cell2.im : 0;
1245
+ v2 = re2 * re2 + im2 * im2;
1246
+ }
1247
+
1248
+ sumProduct += v1 * v2;
1249
+ sumSq1 += v1 * v1;
1250
+ sumSq2 += v2 * v2;
1251
+ }
1252
+ }
1253
+
1254
+ const norm = Math.sqrt(sumSq1) * Math.sqrt(sumSq2);
1255
+ return norm > 1e-10 ? sumProduct / norm : 0;
1256
+ }
1257
+
1258
+ /**
1259
+ * Apply decay to all memories
1260
+ */
1261
+ decay() {
1262
+ for (const memory of this.memories) {
1263
+ memory.strength *= (1 - this.decayRate);
1264
+ }
1265
+
1266
+ // Remove very weak memories
1267
+ this.memories = this.memories.filter(m => m.strength > 0.1);
1268
+ }
1269
+
1270
+ /**
1271
+ * Prune memories to capacity
1272
+ * Removes weakest or oldest memories
1273
+ */
1274
+ prune() {
1275
+ if (this.memories.length <= this.maxMemories) return;
1276
+
1277
+ // Sort by strength * accessCount
1278
+ this.memories.sort((a, b) =>
1279
+ (b.strength * (b.accessCount + 1)) -
1280
+ (a.strength * (a.accessCount + 1))
1281
+ );
1282
+
1283
+ // Keep top memories
1284
+ this.memories = this.memories.slice(0, this.maxMemories);
1285
+ }
1286
+
1287
+ /**
1288
+ * Find all memories above similarity threshold
1289
+ * @param {PrimeState|Object} cue - Retrieval cue
1290
+ * @param {number} [threshold=0.3] - Minimum similarity
1291
+ * @returns {Array<Object>} Matching memories
1292
+ */
1293
+ findSimilar(cue, threshold = 0.3) {
1294
+ const cueEncoder = new HolographicEncoder(
1295
+ this.encoder.gridSize,
1296
+ this.encoder.primes.slice()
1297
+ );
1298
+ cueEncoder.project(cue);
1299
+
1300
+ const results = [];
1301
+ for (const memory of this.memories) {
1302
+ const score = this.correlate(cueEncoder, memory.encoder);
1303
+ if (score > threshold) {
1304
+ results.push({
1305
+ state: memory.encoder.reconstructToState(),
1306
+ metadata: memory.metadata,
1307
+ score,
1308
+ strength: memory.strength
1309
+ });
1310
+ }
1311
+ }
1312
+
1313
+ return results.sort((a, b) => b.score - a.score);
1314
+ }
1315
+
1316
+ /**
1317
+ * Get memory count
1318
+ * @returns {number} Number of stored memories
1319
+ */
1320
+ get count() {
1321
+ return this.memories.length;
1322
+ }
1323
+
1324
+ /**
1325
+ * Clear all memories
1326
+ */
1327
+ clear() {
1328
+ this.memories = [];
1329
+ }
1330
+
1331
+ /**
1332
+ * Serialize to JSON (compact form)
1333
+ * @returns {Object} JSON representation
1334
+ */
1335
+ toJSON() {
1336
+ return {
1337
+ gridSize: this.encoder.gridSize,
1338
+ primes: this.encoder.primes,
1339
+ memories: this.memories.map(m => ({
1340
+ state: m.encoder.getState(),
1341
+ metadata: m.metadata,
1342
+ timestamp: m.timestamp,
1343
+ accessCount: m.accessCount,
1344
+ strength: m.strength
1345
+ }))
1346
+ };
1347
+ }
1348
+
1349
+ /**
1350
+ * Load from JSON
1351
+ * @param {Object} data - Serialized memory
1352
+ * @returns {HolographicMemory} Restored memory
1353
+ */
1354
+ static fromJSON(data) {
1355
+ const memory = new HolographicMemory(data.gridSize, data.primes);
1356
+
1357
+ for (const saved of data.memories) {
1358
+ const encoder = new HolographicEncoder(data.gridSize, data.primes);
1359
+ encoder.loadState(saved.state);
1360
+
1361
+ memory.memories.push({
1362
+ encoder,
1363
+ metadata: saved.metadata,
1364
+ timestamp: saved.timestamp,
1365
+ accessCount: saved.accessCount,
1366
+ strength: saved.strength
1367
+ });
1368
+ }
1369
+
1370
+ return memory;
1371
+ }
1372
+ }
1373
+
1374
+ // ════════════════════════════════════════════════════════════════════
1375
+ // HOLOGRAPHIC SIMILARITY
1376
+ // ════════════════════════════════════════════════════════════════════
1377
+
1378
+ /**
1379
+ * Pattern similarity using holographic comparison
1380
+ */
1381
+ class HolographicSimilarity {
1382
+ /**
1383
+ * Create a similarity calculator
1384
+ * @param {number} [gridSize=32] - Holographic grid size
1385
+ * @param {number|Array} [primes=32] - Primes to use
1386
+ */
1387
+ constructor(gridSize = 32, primes = 32) {
1388
+ this.encoder1 = new HolographicEncoder(gridSize, primes);
1389
+ this.encoder2 = new HolographicEncoder(gridSize, primes);
1390
+ }
1391
+
1392
+ /**
1393
+ * Compute holographic similarity between two states
1394
+ * @param {PrimeState|Object} state1 - First state
1395
+ * @param {PrimeState|Object} state2 - Second state
1396
+ * @returns {number} Similarity score (0-1)
1397
+ */
1398
+ similarity(state1, state2) {
1399
+ this.encoder1.project(state1);
1400
+ this.encoder2.project(state2);
1401
+
1402
+ // Compute intensity correlation
1403
+ const I1 = this.encoder1.intensity();
1404
+ const I2 = this.encoder2.intensity();
1405
+
1406
+ let sumProduct = 0;
1407
+ let sumSq1 = 0;
1408
+ let sumSq2 = 0;
1409
+
1410
+ for (let x = 0; x < this.encoder1.gridSize; x++) {
1411
+ for (let y = 0; y < this.encoder1.gridSize; y++) {
1412
+ sumProduct += I1[x][y] * I2[x][y];
1413
+ sumSq1 += I1[x][y] * I1[x][y];
1414
+ sumSq2 += I2[x][y] * I2[x][y];
1415
+ }
1416
+ }
1417
+
1418
+ const norm = Math.sqrt(sumSq1) * Math.sqrt(sumSq2);
1419
+ return norm > 1e-10 ? sumProduct / norm : 0;
1420
+ }
1421
+
1422
+ /**
1423
+ * Compute difference pattern
1424
+ * @param {PrimeState|Object} state1 - First state
1425
+ * @param {PrimeState|Object} state2 - Second state
1426
+ * @returns {HolographicEncoder} Difference encoder
1427
+ */
1428
+ difference(state1, state2) {
1429
+ this.encoder1.project(state1);
1430
+ this.encoder2.project(state2);
1431
+
1432
+ const diff = this.encoder1.clone();
1433
+
1434
+ for (let x = 0; x < diff.gridSize; x++) {
1435
+ for (let y = 0; y < diff.gridSize; y++) {
1436
+ diff.field[x][y] = new Complex(
1437
+ diff.field[x][y].re - this.encoder2.field[x][y].re,
1438
+ diff.field[x][y].im - this.encoder2.field[x][y].im
1439
+ );
1440
+ }
1441
+ }
1442
+
1443
+ return diff;
1444
+ }
1445
+ }
1446
+
1447
+ // ════════════════════════════════════════════════════════════════════
1448
+ // EXPORTS
1449
+ // ════════════════════════════════════════════════════════════════════
1450
+
1451
+ export {
1452
+ TickGate,
1453
+ StabilizationController,
1454
+ HolographicEncoder,
1455
+ HolographicMemory,
1456
+ HolographicSimilarity
1457
+ };
1458
+
1459
+ export default {
1460
+ TickGate,
1461
+ StabilizationController,
1462
+ HolographicEncoder,
1463
+ HolographicMemory,
1464
+ HolographicSimilarity
1465
+ };