@aleph-ai/tinyaleph 1.5.7 → 1.6.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.
Files changed (109) hide show
  1. package/core/alexander-module.js +1469 -0
  2. package/core/arithmetic-link-kernel.js +1338 -0
  3. package/core/index.js +95 -2
  4. package/examples/01-hello-world.js +69 -0
  5. package/examples/02-basic-hash.js +90 -0
  6. package/examples/02-observer-stack.js +385 -0
  7. package/examples/03-quantum-coin.js +136 -0
  8. package/examples/05-symbolic-resonance.js +146 -0
  9. package/examples/06-symbol-database.js +150 -0
  10. package/examples/07-semantic-inference.js +223 -0
  11. package/examples/08-compound-symbols.js +219 -0
  12. package/examples/README.md +170 -0
  13. package/examples/ai/01-embeddings.js +155 -0
  14. package/examples/ai/02-semantic-memory.js +243 -0
  15. package/examples/ai/03-reasoning.js +243 -0
  16. package/examples/ai/04-knowledge-graph.js +279 -0
  17. package/examples/ai/05-llm-integration.js +333 -0
  18. package/examples/ai/06-agent.js +294 -0
  19. package/examples/ai/07-hybrid-ai.js +223 -0
  20. package/examples/ai/08-entropy-reasoning.js +259 -0
  21. package/examples/ai/09-concept-learning.js +271 -0
  22. package/examples/ai/10-prompt-primes.js +312 -0
  23. package/examples/ai/11-rag.js +332 -0
  24. package/examples/ai/12-neuro-symbolic.js +321 -0
  25. package/examples/ai/README.md +80 -0
  26. package/examples/arithmetic-topology/01-legendre-symbol.js +78 -0
  27. package/examples/arithmetic-topology/02-redei-symbol.js +126 -0
  28. package/examples/arithmetic-topology/03-alk-kuramoto.js +138 -0
  29. package/examples/arithmetic-topology/04-alexander-module.js +117 -0
  30. package/examples/arithmetic-topology/05-signature-memory.js +118 -0
  31. package/examples/arithmetic-topology/README.md +291 -0
  32. package/examples/bioinformatics/01-dna-encoding.js +108 -0
  33. package/examples/bioinformatics/02-central-dogma.js +162 -0
  34. package/examples/bioinformatics/03-protein-folding.js +206 -0
  35. package/examples/bioinformatics/04-dna-computing.js +192 -0
  36. package/examples/bioinformatics/05-molecular-binding.js +209 -0
  37. package/examples/chat.js +105 -0
  38. package/examples/crt-homology/01-residue-encoding.js +87 -0
  39. package/examples/crt-homology/02-birkhoff-attention.js +100 -0
  40. package/examples/crt-homology/03-homology-loss.js +132 -0
  41. package/examples/crt-homology/04-crt-resoformer.js +132 -0
  42. package/examples/crt-homology/README.md +67 -0
  43. package/examples/crypto/01-password-hash.js +210 -0
  44. package/examples/crypto/02-key-derivation.js +210 -0
  45. package/examples/crypto/03-hmac.js +229 -0
  46. package/examples/crypto/04-file-integrity.js +263 -0
  47. package/examples/crypto/05-content-hash.js +263 -0
  48. package/examples/crypto/README.md +99 -0
  49. package/examples/demo-modular.js +223 -0
  50. package/examples/demo-two-layer.js +196 -0
  51. package/examples/discrete/01-integer-sine-table.js +120 -0
  52. package/examples/discrete/02-codebook-tunneling.js +118 -0
  53. package/examples/discrete/03-canonical-fusion.js +135 -0
  54. package/examples/discrete/04-tick-gate.js +139 -0
  55. package/examples/discrete/README.md +142 -0
  56. package/examples/formal-semantics/01-typed-terms.js +156 -0
  57. package/examples/formal-semantics/02-reduction.js +202 -0
  58. package/examples/formal-semantics/03-lambda-translation.js +206 -0
  59. package/examples/formal-semantics/04-enochian-language.js +257 -0
  60. package/examples/formal-semantics/README.md +98 -0
  61. package/examples/math/01-quaternions.js +237 -0
  62. package/examples/math/02-octonions.js +192 -0
  63. package/examples/math/03-prime-factorization.js +215 -0
  64. package/examples/math/04-vector-spaces.js +210 -0
  65. package/examples/math/05-gaussian-primes.js +234 -0
  66. package/examples/math/README.md +93 -0
  67. package/examples/physics/01-oscillator.js +177 -0
  68. package/examples/physics/02-lyapunov.js +201 -0
  69. package/examples/physics/03-collapse.js +183 -0
  70. package/examples/physics/04-kuramoto.js +212 -0
  71. package/examples/physics/05-entropy.js +226 -0
  72. package/examples/physics/05-sync-models.js +298 -0
  73. package/examples/physics/06-primeon-ladder.js +233 -0
  74. package/examples/physics/07-kuramoto-coupled-ladder.js +298 -0
  75. package/examples/physics/README.md +126 -0
  76. package/examples/resonance/01-prime-hilbert-space.js +140 -0
  77. package/examples/resonance/02-prime-resonance-network.js +221 -0
  78. package/examples/resonance/03-resoformer.js +349 -0
  79. package/examples/resonance/04-resoformer-training.js +329 -0
  80. package/examples/resonance/05-language-model.js +484 -0
  81. package/examples/resonance/README.md +238 -0
  82. package/examples/run-examples.js +417 -0
  83. package/examples/scientific/01-single-qubit.js +185 -0
  84. package/examples/scientific/02-two-qubit.js +209 -0
  85. package/examples/scientific/03-quantum-circuits.js +270 -0
  86. package/examples/scientific/04-measurement.js +229 -0
  87. package/examples/scientific/05-algorithms.js +245 -0
  88. package/examples/scientific/06-random.js +225 -0
  89. package/examples/scientific/07-wavefunction.js +192 -0
  90. package/examples/scientific/README.md +118 -0
  91. package/examples/semantic/01-vocabulary.js +186 -0
  92. package/examples/semantic/02-similarity.js +263 -0
  93. package/examples/semantic/03-word-algebra.js +295 -0
  94. package/examples/semantic/04-clustering.js +348 -0
  95. package/examples/semantic/05-classification.js +386 -0
  96. package/examples/semantic/06-dna-encoding.js +228 -0
  97. package/examples/semantic/07-search.js +304 -0
  98. package/examples/semantic/08-qa-system.js +278 -0
  99. package/examples/semantic/README.md +116 -0
  100. package/examples/topology/01-108-invariant.js +81 -0
  101. package/examples/topology/02-trefoil-constants.js +112 -0
  102. package/examples/topology/03-gauge-symmetry.js +112 -0
  103. package/examples/topology/04-free-energy-dynamics.js +124 -0
  104. package/examples/topology/README.md +129 -0
  105. package/index.js +32 -0
  106. package/modular.js +63 -2
  107. package/package.json +8 -3
  108. package/physics/alk-kuramoto.js +817 -0
  109. package/physics/index.js +23 -2
@@ -0,0 +1,817 @@
1
+ /**
2
+ * ALK-Kuramoto Integration
3
+ *
4
+ * Extends Kuramoto oscillator dynamics with Arithmetic Link Kernel couplings.
5
+ * Uses ALK's J matrix for pairwise coupling and K³ tensor for triadic phase terms.
6
+ *
7
+ * Key equations:
8
+ * Standard Kuramoto: dθᵢ/dt = ωᵢ + (K/N) Σⱼ sin(θⱼ - θᵢ)
9
+ *
10
+ * ALK-Kuramoto: dθᵢ/dt = ωᵢ + Σⱼ Jᵢⱼ sin(θⱼ - θᵢ)
11
+ * + Σⱼ<ₖ K³ᵢⱼₖ sin(θⱼ + θₖ - 2θᵢ)
12
+ *
13
+ * The triadic term sin(θⱼ + θₖ - 2θᵢ) captures irreducible three-body
14
+ * phase correlations (Borromean coherence).
15
+ *
16
+ * @module physics/alk-kuramoto
17
+ */
18
+
19
+ 'use strict';
20
+
21
+ import { KuramotoModel } from './kuramoto.js';
22
+ import { OscillatorBank } from './oscillator.js';
23
+ import { ArithmeticLinkKernel } from '../core/arithmetic-link-kernel.js';
24
+
25
+ // ============================================================================
26
+ // ALK KURAMOTO MODEL
27
+ // ============================================================================
28
+
29
+ /**
30
+ * ALK-Kuramoto Model
31
+ *
32
+ * Extends standard Kuramoto with:
33
+ * - Pairwise coupling from Legendre symbols (J matrix)
34
+ * - Triadic coupling from Rédei symbols (K³ tensor)
35
+ * - Higher-order couplings from Milnor invariants (Kⁿ)
36
+ */
37
+ class ALKKuramotoModel {
38
+ /**
39
+ * Create an ALK-Kuramoto model
40
+ *
41
+ * @param {OscillatorBank|number[]} oscillators - Oscillator bank or natural frequencies
42
+ * @param {Object} alk - ArithmeticLinkKernel instance
43
+ * @param {Object} options - Configuration
44
+ * @param {number} options.couplingScale - Scale factor for J matrix (default: 0.1)
45
+ * @param {number} options.triadicScale - Scale factor for K³ tensor (default: 0.05)
46
+ * @param {boolean} options.useTriadic - Enable triadic coupling (default: true)
47
+ * @param {boolean} options.useHigherOrder - Enable n>3 couplings (default: false)
48
+ * @param {number} options.dt - Time step (default: 0.01)
49
+ */
50
+ constructor(oscillators, alk, options = {}) {
51
+ // Handle oscillator input
52
+ if (oscillators instanceof OscillatorBank) {
53
+ this.bank = oscillators;
54
+ this.N = oscillators.size;
55
+ this.omega = new Float64Array(this.N);
56
+ for (let i = 0; i < this.N; i++) {
57
+ this.omega[i] = oscillators.oscillators[i].frequency;
58
+ }
59
+ } else if (Array.isArray(oscillators)) {
60
+ this.omega = Float64Array.from(oscillators);
61
+ this.N = oscillators.length;
62
+ this.bank = null;
63
+ } else {
64
+ throw new Error('oscillators must be OscillatorBank or frequency array');
65
+ }
66
+
67
+ // Store ALK
68
+ this.alk = alk;
69
+
70
+ // Ensure prime count matches oscillator count
71
+ if (alk.r !== this.N) {
72
+ console.warn(`ALK has ${alk.r} primes but model has ${this.N} oscillators. Using min.`);
73
+ this.N = Math.min(alk.r, this.N);
74
+ }
75
+
76
+ // Configuration
77
+ this.couplingScale = options.couplingScale ?? 0.1;
78
+ this.triadicScale = options.triadicScale ?? 0.05;
79
+ this.useTriadic = options.useTriadic ?? true;
80
+ this.useHigherOrder = options.useHigherOrder ?? false;
81
+ this.dt = options.dt ?? 0.01;
82
+
83
+ // State: phases
84
+ this.theta = new Float64Array(this.N);
85
+ for (let i = 0; i < this.N; i++) {
86
+ this.theta[i] = Math.random() * 2 * Math.PI;
87
+ }
88
+
89
+ // Time tracking
90
+ this.time = 0;
91
+ this.steps = 0;
92
+
93
+ // Cache ALK matrices
94
+ this._J = null;
95
+ this._K3Entries = null;
96
+
97
+ // History for analysis
98
+ this.history = [];
99
+ this.maxHistory = options.maxHistory ?? 1000;
100
+ }
101
+
102
+ /**
103
+ * Get cached J matrix (scaled)
104
+ */
105
+ get J() {
106
+ if (!this._J) {
107
+ const rawJ = this.alk.J;
108
+ this._J = [];
109
+ for (let i = 0; i < this.N; i++) {
110
+ this._J[i] = new Float64Array(this.N);
111
+ for (let j = 0; j < this.N; j++) {
112
+ this._J[i][j] = this.couplingScale * rawJ[i][j];
113
+ }
114
+ }
115
+ }
116
+ return this._J;
117
+ }
118
+
119
+ /**
120
+ * Get cached K³ entries (scaled)
121
+ */
122
+ get K3Entries() {
123
+ if (!this._K3Entries) {
124
+ this._K3Entries = [];
125
+ const K3 = this.alk.K3;
126
+ for (const [, entry] of K3.entries) {
127
+ const [i, j, k] = entry.indices;
128
+ if (i < this.N && j < this.N && k < this.N) {
129
+ this._K3Entries.push({
130
+ i, j, k,
131
+ value: this.triadicScale * entry.value
132
+ });
133
+ }
134
+ }
135
+ }
136
+ return this._K3Entries;
137
+ }
138
+
139
+ /**
140
+ * Compute pairwise coupling term for oscillator i
141
+ * Σⱼ Jᵢⱼ sin(θⱼ - θᵢ)
142
+ *
143
+ * @param {number} i - Oscillator index
144
+ * @returns {number} Pairwise coupling contribution
145
+ */
146
+ _pairwiseCoupling(i) {
147
+ const J = this.J;
148
+ let sum = 0;
149
+
150
+ for (let j = 0; j < this.N; j++) {
151
+ if (i !== j && J[i][j] !== 0) {
152
+ sum += J[i][j] * Math.sin(this.theta[j] - this.theta[i]);
153
+ }
154
+ }
155
+
156
+ return sum;
157
+ }
158
+
159
+ /**
160
+ * Compute triadic coupling term for oscillator i
161
+ * Σⱼ<ₖ K³ᵢⱼₖ sin(θⱼ + θₖ - 2θᵢ)
162
+ *
163
+ * This captures Borromean-type coherence where three oscillators
164
+ * lock together without pairwise locking.
165
+ *
166
+ * @param {number} i - Oscillator index
167
+ * @returns {number} Triadic coupling contribution
168
+ */
169
+ _triadicCoupling(i) {
170
+ if (!this.useTriadic) return 0;
171
+
172
+ let sum = 0;
173
+
174
+ for (const entry of this.K3Entries) {
175
+ // Each entry affects all three oscillators in the triple
176
+ if (entry.i === i) {
177
+ // Oscillator i is first in triple
178
+ sum += entry.value * Math.sin(this.theta[entry.j] + this.theta[entry.k] - 2 * this.theta[i]);
179
+ } else if (entry.j === i) {
180
+ // Oscillator i is second
181
+ sum += entry.value * Math.sin(this.theta[entry.i] + this.theta[entry.k] - 2 * this.theta[i]);
182
+ } else if (entry.k === i) {
183
+ // Oscillator i is third
184
+ sum += entry.value * Math.sin(this.theta[entry.i] + this.theta[entry.j] - 2 * this.theta[i]);
185
+ }
186
+ }
187
+
188
+ return sum;
189
+ }
190
+
191
+ /**
192
+ * Compute full dθᵢ/dt
193
+ *
194
+ * @param {number} i - Oscillator index
195
+ * @returns {number} Phase derivative
196
+ */
197
+ _dtheta(i) {
198
+ return this.omega[i] + this._pairwiseCoupling(i) + this._triadicCoupling(i);
199
+ }
200
+
201
+ /**
202
+ * Euler integration step
203
+ *
204
+ * @param {number} dt - Time step (uses this.dt if not provided)
205
+ */
206
+ step(dt = this.dt) {
207
+ const newTheta = new Float64Array(this.N);
208
+
209
+ // Compute derivatives
210
+ for (let i = 0; i < this.N; i++) {
211
+ const dth = this._dtheta(i);
212
+ newTheta[i] = this.theta[i] + dth * dt;
213
+ // Wrap to [0, 2π)
214
+ newTheta[i] = ((newTheta[i] % (2 * Math.PI)) + 2 * Math.PI) % (2 * Math.PI);
215
+ }
216
+
217
+ this.theta = newTheta;
218
+ this.time += dt;
219
+ this.steps++;
220
+
221
+ // Update oscillator bank if present
222
+ if (this.bank) {
223
+ for (let i = 0; i < this.N; i++) {
224
+ this.bank.oscillators[i].phase = this.theta[i];
225
+ }
226
+ }
227
+
228
+ return this;
229
+ }
230
+
231
+ /**
232
+ * Runge-Kutta 4 integration step (more accurate)
233
+ *
234
+ * @param {number} dt - Time step
235
+ */
236
+ stepRK4(dt = this.dt) {
237
+ const k1 = new Float64Array(this.N);
238
+ const k2 = new Float64Array(this.N);
239
+ const k3 = new Float64Array(this.N);
240
+ const k4 = new Float64Array(this.N);
241
+ const temp = new Float64Array(this.N);
242
+
243
+ // k1 = f(t, y)
244
+ for (let i = 0; i < this.N; i++) {
245
+ k1[i] = this._dtheta(i);
246
+ }
247
+
248
+ // k2 = f(t + dt/2, y + k1*dt/2)
249
+ for (let i = 0; i < this.N; i++) {
250
+ temp[i] = this.theta[i] + k1[i] * dt / 2;
251
+ }
252
+ const savedTheta = this.theta;
253
+ this.theta = temp;
254
+ for (let i = 0; i < this.N; i++) {
255
+ k2[i] = this._dtheta(i);
256
+ }
257
+
258
+ // k3 = f(t + dt/2, y + k2*dt/2)
259
+ for (let i = 0; i < this.N; i++) {
260
+ temp[i] = savedTheta[i] + k2[i] * dt / 2;
261
+ }
262
+ this.theta = temp;
263
+ for (let i = 0; i < this.N; i++) {
264
+ k3[i] = this._dtheta(i);
265
+ }
266
+
267
+ // k4 = f(t + dt, y + k3*dt)
268
+ for (let i = 0; i < this.N; i++) {
269
+ temp[i] = savedTheta[i] + k3[i] * dt;
270
+ }
271
+ this.theta = temp;
272
+ for (let i = 0; i < this.N; i++) {
273
+ k4[i] = this._dtheta(i);
274
+ }
275
+
276
+ // y(t + dt) = y(t) + (k1 + 2*k2 + 2*k3 + k4) * dt / 6
277
+ for (let i = 0; i < this.N; i++) {
278
+ this.theta[i] = savedTheta[i] + (k1[i] + 2*k2[i] + 2*k3[i] + k4[i]) * dt / 6;
279
+ this.theta[i] = ((this.theta[i] % (2 * Math.PI)) + 2 * Math.PI) % (2 * Math.PI);
280
+ }
281
+
282
+ this.time += dt;
283
+ this.steps++;
284
+
285
+ if (this.bank) {
286
+ for (let i = 0; i < this.N; i++) {
287
+ this.bank.oscillators[i].phase = this.theta[i];
288
+ }
289
+ }
290
+
291
+ return this;
292
+ }
293
+
294
+ /**
295
+ * Evolve for multiple steps
296
+ *
297
+ * @param {number} numSteps - Number of steps
298
+ * @param {boolean} useRK4 - Use RK4 integration (default: false)
299
+ * @param {boolean} record - Record history (default: false)
300
+ */
301
+ evolve(numSteps, useRK4 = false, record = false) {
302
+ const stepFn = useRK4 ? () => this.stepRK4() : () => this.step();
303
+
304
+ for (let i = 0; i < numSteps; i++) {
305
+ stepFn();
306
+
307
+ if (record) {
308
+ this._recordState();
309
+ }
310
+ }
311
+
312
+ return this;
313
+ }
314
+
315
+ /**
316
+ * Record current state to history
317
+ * @private
318
+ */
319
+ _recordState() {
320
+ const state = {
321
+ time: this.time,
322
+ steps: this.steps,
323
+ theta: Float64Array.from(this.theta),
324
+ orderParameter: this.orderParameter(),
325
+ triadicCoherence: this.triadicCoherence()
326
+ };
327
+
328
+ this.history.push(state);
329
+
330
+ // Limit history size
331
+ while (this.history.length > this.maxHistory) {
332
+ this.history.shift();
333
+ }
334
+ }
335
+
336
+ /**
337
+ * Kuramoto order parameter r = |1/N Σⱼ e^(iθⱼ)|
338
+ *
339
+ * r = 1: Perfect synchronization
340
+ * r = 0: Incoherent (random phases)
341
+ *
342
+ * @returns {number} Order parameter in [0, 1]
343
+ */
344
+ orderParameter() {
345
+ let realSum = 0;
346
+ let imagSum = 0;
347
+
348
+ for (let i = 0; i < this.N; i++) {
349
+ realSum += Math.cos(this.theta[i]);
350
+ imagSum += Math.sin(this.theta[i]);
351
+ }
352
+
353
+ return Math.sqrt(realSum * realSum + imagSum * imagSum) / this.N;
354
+ }
355
+
356
+ /**
357
+ * Mean phase Ψ = arg(Σⱼ e^(iθⱼ))
358
+ *
359
+ * @returns {number} Mean phase in [0, 2π)
360
+ */
361
+ meanPhase() {
362
+ let realSum = 0;
363
+ let imagSum = 0;
364
+
365
+ for (let i = 0; i < this.N; i++) {
366
+ realSum += Math.cos(this.theta[i]);
367
+ imagSum += Math.sin(this.theta[i]);
368
+ }
369
+
370
+ const phase = Math.atan2(imagSum, realSum);
371
+ return ((phase % (2 * Math.PI)) + 2 * Math.PI) % (2 * Math.PI);
372
+ }
373
+
374
+ /**
375
+ * Triadic coherence measure
376
+ * Measures phase coherence within Borromean triples
377
+ *
378
+ * For triple (i, j, k): coherence = |e^(i(θᵢ + θⱼ + θₖ))|
379
+ *
380
+ * @returns {number} Mean triadic coherence in [0, 1]
381
+ */
382
+ triadicCoherence() {
383
+ const borromean = this.alk.findBorromeanTriples();
384
+ if (borromean.length === 0) return 0;
385
+
386
+ let totalCoherence = 0;
387
+
388
+ for (const triple of borromean) {
389
+ const [i, j, k] = triple.indices;
390
+ if (i >= this.N || j >= this.N || k >= this.N) continue;
391
+
392
+ const sumPhase = this.theta[i] + this.theta[j] + this.theta[k];
393
+ // Triadic coherence: how close is sum to 0 mod 2π?
394
+ const coherence = Math.cos(sumPhase);
395
+ totalCoherence += Math.abs(coherence);
396
+ }
397
+
398
+ return totalCoherence / borromean.length;
399
+ }
400
+
401
+ /**
402
+ * Phase variance (measure of incoherence)
403
+ *
404
+ * @returns {number} Circular variance in [0, 1]
405
+ */
406
+ phaseVariance() {
407
+ return 1 - this.orderParameter();
408
+ }
409
+
410
+ /**
411
+ * Pairwise phase locking detection
412
+ * Returns pairs (i, j) where |θᵢ - θⱼ| < threshold
413
+ *
414
+ * @param {number} threshold - Phase difference threshold (default: π/6)
415
+ * @returns {Array} Locked pairs [{i, j, phaseDiff}]
416
+ */
417
+ findLockedPairs(threshold = Math.PI / 6) {
418
+ const locked = [];
419
+
420
+ for (let i = 0; i < this.N; i++) {
421
+ for (let j = i + 1; j < this.N; j++) {
422
+ let diff = Math.abs(this.theta[i] - this.theta[j]);
423
+ diff = Math.min(diff, 2 * Math.PI - diff);
424
+
425
+ if (diff < threshold) {
426
+ locked.push({
427
+ i, j,
428
+ primes: [this.alk.primes[i], this.alk.primes[j]],
429
+ phaseDiff: diff,
430
+ coupling: this.J[i][j]
431
+ });
432
+ }
433
+ }
434
+ }
435
+
436
+ return locked;
437
+ }
438
+
439
+ /**
440
+ * Find locked triads (three oscillators with mutual phase coherence)
441
+ *
442
+ * @param {number} threshold - Phase threshold
443
+ * @returns {Array} Locked triads
444
+ */
445
+ findLockedTriads(threshold = Math.PI / 4) {
446
+ const locked = [];
447
+
448
+ for (const entry of this.K3Entries) {
449
+ const { i, j, k } = entry;
450
+ if (i >= this.N || j >= this.N || k >= this.N) continue;
451
+
452
+ const sumPhase = this.theta[i] + this.theta[j] + this.theta[k];
453
+ const coherence = Math.abs(Math.cos(sumPhase));
454
+
455
+ if (coherence > Math.cos(threshold)) {
456
+ locked.push({
457
+ indices: [i, j, k],
458
+ primes: [this.alk.primes[i], this.alk.primes[j], this.alk.primes[k]],
459
+ sumPhase,
460
+ coherence,
461
+ coupling: entry.value
462
+ });
463
+ }
464
+ }
465
+
466
+ return locked;
467
+ }
468
+
469
+ /**
470
+ * Get current state as object
471
+ */
472
+ getState() {
473
+ return {
474
+ time: this.time,
475
+ steps: this.steps,
476
+ N: this.N,
477
+ theta: Array.from(this.theta),
478
+ omega: Array.from(this.omega),
479
+ orderParameter: this.orderParameter(),
480
+ meanPhase: this.meanPhase(),
481
+ triadicCoherence: this.triadicCoherence(),
482
+ lockedPairs: this.findLockedPairs(),
483
+ lockedTriads: this.findLockedTriads()
484
+ };
485
+ }
486
+
487
+ /**
488
+ * Set phases directly
489
+ *
490
+ * @param {number[]} phases - Array of phases
491
+ */
492
+ setPhases(phases) {
493
+ for (let i = 0; i < Math.min(phases.length, this.N); i++) {
494
+ this.theta[i] = phases[i];
495
+ }
496
+ return this;
497
+ }
498
+
499
+ /**
500
+ * Reset to random phases
501
+ */
502
+ reset() {
503
+ for (let i = 0; i < this.N; i++) {
504
+ this.theta[i] = Math.random() * 2 * Math.PI;
505
+ }
506
+ this.time = 0;
507
+ this.steps = 0;
508
+ this.history = [];
509
+ return this;
510
+ }
511
+
512
+ /**
513
+ * Clone the model
514
+ */
515
+ clone() {
516
+ const clone = new ALKKuramotoModel(Array.from(this.omega), this.alk, {
517
+ couplingScale: this.couplingScale,
518
+ triadicScale: this.triadicScale,
519
+ useTriadic: this.useTriadic,
520
+ useHigherOrder: this.useHigherOrder,
521
+ dt: this.dt
522
+ });
523
+
524
+ clone.theta = Float64Array.from(this.theta);
525
+ clone.time = this.time;
526
+ clone.steps = this.steps;
527
+
528
+ return clone;
529
+ }
530
+ }
531
+
532
+ // ============================================================================
533
+ // ALK NETWORK KURAMOTO
534
+ // ============================================================================
535
+
536
+ /**
537
+ * ALK Network Kuramoto
538
+ *
539
+ * Uses ALK's J matrix as the adjacency/coupling matrix for
540
+ * network-topology aware Kuramoto dynamics.
541
+ */
542
+ class ALKNetworkKuramoto extends ALKKuramotoModel {
543
+ /**
544
+ * Create ALK Network Kuramoto model
545
+ *
546
+ * @param {number[]} frequencies - Natural frequencies
547
+ * @param {Object} alk - ArithmeticLinkKernel
548
+ * @param {Object} options - Configuration
549
+ */
550
+ constructor(frequencies, alk, options = {}) {
551
+ super(frequencies, alk, options);
552
+
553
+ // Use symmetrized coupling for undirected network
554
+ this.useSymmetric = options.useSymmetric ?? true;
555
+
556
+ // Compute effective coupling matrix
557
+ this._effectiveJ = null;
558
+ }
559
+
560
+ /**
561
+ * Get effective coupling matrix
562
+ */
563
+ get effectiveJ() {
564
+ if (!this._effectiveJ) {
565
+ if (this.useSymmetric) {
566
+ // Use (J + J^T) / 2
567
+ const Jsym = this.alk.Jsym;
568
+ this._effectiveJ = [];
569
+ for (let i = 0; i < this.N; i++) {
570
+ this._effectiveJ[i] = new Float64Array(this.N);
571
+ for (let j = 0; j < this.N; j++) {
572
+ this._effectiveJ[i][j] = this.couplingScale * Jsym[i][j];
573
+ }
574
+ }
575
+ } else {
576
+ this._effectiveJ = this.J;
577
+ }
578
+ }
579
+ return this._effectiveJ;
580
+ }
581
+
582
+ /**
583
+ * Override pairwise coupling to use effective J
584
+ */
585
+ _pairwiseCoupling(i) {
586
+ const J = this.effectiveJ;
587
+ let sum = 0;
588
+
589
+ for (let j = 0; j < this.N; j++) {
590
+ if (i !== j && J[i][j] !== 0) {
591
+ sum += J[i][j] * Math.sin(this.theta[j] - this.theta[i]);
592
+ }
593
+ }
594
+
595
+ return sum;
596
+ }
597
+
598
+ /**
599
+ * Compute clustering coefficient based on ALK couplings
600
+ *
601
+ * @returns {number} Mean clustering coefficient
602
+ */
603
+ clusteringCoefficient() {
604
+ const J = this.effectiveJ;
605
+ let totalCC = 0;
606
+ let validNodes = 0;
607
+
608
+ for (let i = 0; i < this.N; i++) {
609
+ // Find neighbors (non-zero coupling)
610
+ const neighbors = [];
611
+ for (let j = 0; j < this.N; j++) {
612
+ if (i !== j && Math.abs(J[i][j]) > 1e-10) {
613
+ neighbors.push(j);
614
+ }
615
+ }
616
+
617
+ const k = neighbors.length;
618
+ if (k < 2) continue;
619
+
620
+ // Count edges between neighbors
621
+ let triangles = 0;
622
+ for (let a = 0; a < k; a++) {
623
+ for (let b = a + 1; b < k; b++) {
624
+ if (Math.abs(J[neighbors[a]][neighbors[b]]) > 1e-10) {
625
+ triangles++;
626
+ }
627
+ }
628
+ }
629
+
630
+ const maxTriangles = k * (k - 1) / 2;
631
+ totalCC += triangles / maxTriangles;
632
+ validNodes++;
633
+ }
634
+
635
+ return validNodes > 0 ? totalCC / validNodes : 0;
636
+ }
637
+
638
+ /**
639
+ * Compute small-world coefficient
640
+ * σ = (C/C_random) / (L/L_random)
641
+ *
642
+ * @returns {number} Small-world coefficient (σ > 1 indicates small-world)
643
+ */
644
+ smallWorldCoefficient() {
645
+ const C = this.clusteringCoefficient();
646
+ const r = this.orderParameter();
647
+
648
+ // Estimate random network values
649
+ const density = this._computeDensity();
650
+ const C_random = density; // Expected for random network
651
+ const L_random = Math.log(this.N); // Approximate for sparse random
652
+
653
+ // Use order parameter as proxy for path length
654
+ // Higher sync → shorter effective path
655
+ const L = 1 / (r + 0.1);
656
+
657
+ if (C_random < 1e-10 || L_random < 1e-10) return 1;
658
+
659
+ return (C / C_random) / (L / L_random);
660
+ }
661
+
662
+ /**
663
+ * Compute network density
664
+ * @private
665
+ */
666
+ _computeDensity() {
667
+ const J = this.effectiveJ;
668
+ let edges = 0;
669
+
670
+ for (let i = 0; i < this.N; i++) {
671
+ for (let j = i + 1; j < this.N; j++) {
672
+ if (Math.abs(J[i][j]) > 1e-10) {
673
+ edges++;
674
+ }
675
+ }
676
+ }
677
+
678
+ const maxEdges = this.N * (this.N - 1) / 2;
679
+ return edges / maxEdges;
680
+ }
681
+ }
682
+
683
+ // ============================================================================
684
+ // FACTORY FUNCTIONS
685
+ // ============================================================================
686
+
687
+ /**
688
+ * Create ALK-Kuramoto model from prime set
689
+ *
690
+ * @param {number[]} primes - Array of primes
691
+ * @param {Object} options - Configuration
692
+ * @returns {ALKKuramotoModel} Model instance
693
+ */
694
+ function createALKKuramoto(primes, options = {}) {
695
+ const alk = new ArithmeticLinkKernel(primes, {
696
+ encoding: options.encoding || 'bipolar'
697
+ });
698
+
699
+ // Generate natural frequencies from primes
700
+ const omega = primes.map(p => Math.log(p));
701
+
702
+ return new ALKKuramotoModel(omega, alk, options);
703
+ }
704
+
705
+ /**
706
+ * Create ALK Network Kuramoto from prime set
707
+ *
708
+ * @param {number[]} primes - Array of primes
709
+ * @param {Object} options - Configuration
710
+ * @returns {ALKNetworkKuramoto} Model instance
711
+ */
712
+ function createALKNetworkKuramoto(primes, options = {}) {
713
+ const alk = new ArithmeticLinkKernel(primes, {
714
+ encoding: options.encoding || 'bipolar'
715
+ });
716
+
717
+ const omega = primes.map(p => Math.log(p));
718
+
719
+ return new ALKNetworkKuramoto(omega, alk, options);
720
+ }
721
+
722
+ /**
723
+ * Run Borromean synchronization experiment
724
+ *
725
+ * Investigates how Borromean triples synchronize differently
726
+ * from pairwise-coupled oscillators.
727
+ *
728
+ * @param {number[]} primes - Array of primes
729
+ * @param {Object} options - Experiment options
730
+ * @returns {Object} Experiment results
731
+ */
732
+ function runBorromeanExperiment(primes, options = {}) {
733
+ const alk = new ArithmeticLinkKernel(primes);
734
+ const borromean = alk.findBorromeanTriples();
735
+
736
+ const steps = options.steps || 1000;
737
+ const dt = options.dt || 0.01;
738
+
739
+ // Create model with triadic coupling
740
+ const modelTriadic = new ALKKuramotoModel(
741
+ primes.map(p => Math.log(p)),
742
+ alk,
743
+ { useTriadic: true, dt }
744
+ );
745
+
746
+ // Create model without triadic coupling
747
+ const modelPairwise = new ALKKuramotoModel(
748
+ primes.map(p => Math.log(p)),
749
+ alk,
750
+ { useTriadic: false, dt }
751
+ );
752
+
753
+ // Same initial conditions
754
+ const initialPhases = primes.map(() => Math.random() * 2 * Math.PI);
755
+ modelTriadic.setPhases(initialPhases);
756
+ modelPairwise.setPhases(initialPhases);
757
+
758
+ // Evolve both
759
+ const triadicHistory = [];
760
+ const pairwiseHistory = [];
761
+
762
+ for (let i = 0; i < steps; i++) {
763
+ modelTriadic.step();
764
+ modelPairwise.step();
765
+
766
+ if (i % 10 === 0) {
767
+ triadicHistory.push({
768
+ time: modelTriadic.time,
769
+ r: modelTriadic.orderParameter(),
770
+ triadic: modelTriadic.triadicCoherence()
771
+ });
772
+ pairwiseHistory.push({
773
+ time: modelPairwise.time,
774
+ r: modelPairwise.orderParameter(),
775
+ triadic: modelPairwise.triadicCoherence()
776
+ });
777
+ }
778
+ }
779
+
780
+ return {
781
+ borromeanTriples: borromean,
782
+ triadicModel: {
783
+ finalR: modelTriadic.orderParameter(),
784
+ finalTriadic: modelTriadic.triadicCoherence(),
785
+ history: triadicHistory
786
+ },
787
+ pairwiseModel: {
788
+ finalR: modelPairwise.orderParameter(),
789
+ finalTriadic: modelPairwise.triadicCoherence(),
790
+ history: pairwiseHistory
791
+ },
792
+ comparison: {
793
+ rDifference: modelTriadic.orderParameter() - modelPairwise.orderParameter(),
794
+ triadicDifference: modelTriadic.triadicCoherence() - modelPairwise.triadicCoherence()
795
+ }
796
+ };
797
+ }
798
+
799
+ // ============================================================================
800
+ // EXPORTS
801
+ // ============================================================================
802
+
803
+ export {
804
+ ALKKuramotoModel,
805
+ ALKNetworkKuramoto,
806
+ createALKKuramoto,
807
+ createALKNetworkKuramoto,
808
+ runBorromeanExperiment
809
+ };
810
+
811
+ export default {
812
+ ALKKuramotoModel,
813
+ ALKNetworkKuramoto,
814
+ createALKKuramoto,
815
+ createALKNetworkKuramoto,
816
+ runBorromeanExperiment
817
+ };