@aleph-ai/tinyaleph 1.1.0 → 1.2.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/physics/index.js CHANGED
@@ -42,6 +42,32 @@ const {
42
42
  createPeerCoupling
43
43
  } = require('./sync-models');
44
44
 
45
+ // Stochastic Kuramoto models
46
+ const {
47
+ StochasticKuramoto,
48
+ ColoredNoiseKuramoto,
49
+ ThermalKuramoto,
50
+ gaussianRandom
51
+ } = require('./stochastic-kuramoto');
52
+
53
+ // Primeon Z-Ladder with canonical U evolution
54
+ const {
55
+ PrimeonZLadderU,
56
+ createPrimeonLadder,
57
+ shannonEntropyNats,
58
+ probsOf,
59
+ normalize: normalizeComplex,
60
+ C: Complex
61
+ } = require('./primeon_z_ladder_u');
62
+
63
+ // Multi-channel Primeon Z-Ladder
64
+ const {
65
+ ZChannel,
66
+ PrimeonZLadderMulti,
67
+ createMultiChannelLadder,
68
+ createAdiabaticSchedule
69
+ } = require('./primeon_z_ladder_multi');
70
+
45
71
  module.exports = {
46
72
  // Oscillators
47
73
  Oscillator,
@@ -57,6 +83,26 @@ module.exports = {
57
83
  createHierarchicalCoupling,
58
84
  createPeerCoupling,
59
85
 
86
+ // Stochastic Kuramoto models
87
+ StochasticKuramoto,
88
+ ColoredNoiseKuramoto,
89
+ ThermalKuramoto,
90
+ gaussianRandom,
91
+
92
+ // Primeon Z-Ladder (canonical U evolution)
93
+ PrimeonZLadderU,
94
+ createPrimeonLadder,
95
+ shannonEntropyNats,
96
+ probsOf,
97
+ normalizeComplex,
98
+ Complex,
99
+
100
+ // Multi-channel Primeon Z-Ladder
101
+ ZChannel,
102
+ PrimeonZLadderMulti,
103
+ createMultiChannelLadder,
104
+ createAdiabaticSchedule,
105
+
60
106
  // Entropy & Information
61
107
  shannonEntropy,
62
108
  stateEntropy,
@@ -0,0 +1,669 @@
1
+ /**
2
+ * Multi-Channel Primeon Z-Ladder
3
+ *
4
+ * Extension of PrimeonZLadderU with multiple Z sectors for hierarchical memory:
5
+ * - Fast Z (working memory): High leak rate, short-term storage
6
+ * - Slow Z (long-term memory): Low leak rate, persistent storage
7
+ * - Permanent Z (archival): No leak, permanent storage
8
+ *
9
+ * Features:
10
+ * - Named Z channels with individual configurations
11
+ * - Cross-channel interference
12
+ * - Channel-specific metrics and snapshots
13
+ * - Time-dependent Hamiltonian support
14
+ * - Shaped excitation pulses
15
+ */
16
+
17
+ 'use strict';
18
+
19
+ const {
20
+ PrimeonZLadderU,
21
+ C,
22
+ shannonEntropyNats,
23
+ probsOf,
24
+ normalize
25
+ } = require('./primeon_z_ladder_u');
26
+
27
+ /**
28
+ * Z Channel configuration
29
+ */
30
+ class ZChannel {
31
+ /**
32
+ * @param {object} config
33
+ * @param {string} config.name - Channel identifier
34
+ * @param {number} config.dz - Internal dimension per rung
35
+ * @param {number} config.leak - Leak rate from core to this channel
36
+ * @param {number} [config.decay=0] - Internal decay rate within channel
37
+ * @param {number} [config.crossCoupling=0] - Coupling to other channels
38
+ */
39
+ constructor(config) {
40
+ this.name = config.name;
41
+ this.dz = config.dz ?? 1;
42
+ this.leak = Math.max(0, Math.min(1, config.leak ?? 0.05));
43
+ this.decay = config.decay ?? 0;
44
+ this.crossCoupling = config.crossCoupling ?? 0;
45
+ this.N = 0; // Set when attached to ladder
46
+
47
+ // State array (initialized when attached)
48
+ this.z = null;
49
+
50
+ // Metrics
51
+ this.totalFlux = 0;
52
+ this.lastFlux = 0;
53
+ }
54
+
55
+ /**
56
+ * Initialize channel state
57
+ * @param {number} N - Number of rungs
58
+ */
59
+ init(N) {
60
+ this.N = N;
61
+ this.z = Array.from({ length: N * this.dz }, () => C.zero());
62
+ this.totalFlux = 0;
63
+ this.lastFlux = 0;
64
+ }
65
+
66
+ /**
67
+ * Reset channel to vacuum
68
+ */
69
+ reset() {
70
+ for (let i = 0; i < this.z.length; i++) {
71
+ this.z[i] = C.zero();
72
+ }
73
+ this.totalFlux = 0;
74
+ this.lastFlux = 0;
75
+ }
76
+
77
+ /**
78
+ * Compute channel metrics
79
+ */
80
+ metrics() {
81
+ const p = probsOf(this.z);
82
+ const H = shannonEntropyNats(p);
83
+
84
+ let norm = 0;
85
+ for (const v of this.z) {
86
+ norm += C.abs2(v);
87
+ }
88
+ norm = Math.sqrt(norm);
89
+
90
+ return {
91
+ name: this.name,
92
+ entropy: H,
93
+ norm,
94
+ totalFlux: this.totalFlux,
95
+ lastFlux: this.lastFlux,
96
+ coherence: 1 / (1 + H)
97
+ };
98
+ }
99
+
100
+ /**
101
+ * Get state snapshot
102
+ */
103
+ snapshot() {
104
+ return {
105
+ name: this.name,
106
+ dz: this.dz,
107
+ leak: this.leak,
108
+ decay: this.decay,
109
+ z: this.z.map(v => ({ re: v.re, im: v.im })),
110
+ ...this.metrics()
111
+ };
112
+ }
113
+
114
+ /**
115
+ * Restore from snapshot
116
+ */
117
+ restore(snap) {
118
+ this.z = snap.z.map(v => new C(v.re, v.im));
119
+ this.totalFlux = snap.totalFlux;
120
+ this.lastFlux = snap.lastFlux;
121
+ }
122
+ }
123
+
124
+ /**
125
+ * PrimeonZLadderMulti - Multi-channel Z-ladder with hierarchical memory
126
+ */
127
+ class PrimeonZLadderMulti {
128
+ /**
129
+ * @param {object} opts
130
+ * @param {number} opts.N - Number of ladder rungs
131
+ * @param {number} [opts.d=1] - Core internal dimension per rung
132
+ * @param {number} [opts.J=0.25] - Nearest-neighbor coupling strength
133
+ * @param {object[]} opts.zChannels - Array of channel configurations
134
+ * @param {boolean} [opts.periodic=true] - Periodic boundary conditions
135
+ * @param {Function} [opts.Jt=null] - Time-dependent J function J(t)
136
+ */
137
+ constructor(opts) {
138
+ this.N = opts.N;
139
+ this.d = opts.d ?? 1;
140
+ this.J = opts.J ?? 0.25;
141
+ this.J0 = this.J; // Store initial J for Jt reference
142
+ this.periodic = opts.periodic ?? true;
143
+ this.Jt = opts.Jt ?? null; // Time-dependent Hamiltonian
144
+
145
+ // Core state
146
+ this.psi = Array.from({ length: this.N * this.d }, () => C.zero());
147
+
148
+ // Z channels
149
+ this.channels = new Map();
150
+ const channelConfigs = opts.zChannels || [
151
+ { name: 'fast', dz: 1, leak: 0.2 },
152
+ { name: 'slow', dz: 1, leak: 0.02 },
153
+ { name: 'permanent', dz: 1, leak: 0.001, decay: 0 }
154
+ ];
155
+
156
+ for (const config of channelConfigs) {
157
+ const channel = new ZChannel(config);
158
+ channel.init(this.N);
159
+ this.channels.set(config.name, channel);
160
+ }
161
+
162
+ // Metrics
163
+ this.t = 0;
164
+ this.stepCount = 0;
165
+ }
166
+
167
+ /**
168
+ * Get a specific Z channel
169
+ * @param {string} name - Channel name
170
+ */
171
+ getChannel(name) {
172
+ return this.channels.get(name);
173
+ }
174
+
175
+ /**
176
+ * Get all channel names
177
+ */
178
+ getChannelNames() {
179
+ return Array.from(this.channels.keys());
180
+ }
181
+
182
+ /**
183
+ * Reset all state to vacuum
184
+ */
185
+ reset() {
186
+ for (let i = 0; i < this.psi.length; i++) {
187
+ this.psi[i] = C.zero();
188
+ }
189
+
190
+ for (const channel of this.channels.values()) {
191
+ channel.reset();
192
+ }
193
+
194
+ this.t = 0;
195
+ this.stepCount = 0;
196
+ }
197
+
198
+ /**
199
+ * Get current coupling strength (may be time-dependent)
200
+ */
201
+ getCurrentJ() {
202
+ if (this.Jt) {
203
+ return this.Jt(this.t);
204
+ }
205
+ return this.J;
206
+ }
207
+
208
+ /**
209
+ * Excite a specific rung
210
+ * @param {number} n - Rung index
211
+ * @param {C} [amp] - Complex amplitude
212
+ * @param {number} [k=0] - Internal index within rung
213
+ */
214
+ exciteRung(n, amp = new C(1, 0), k = 0) {
215
+ const i = ((n % this.N) + this.N) % this.N * this.d + (k % this.d);
216
+ this.psi[i] = amp;
217
+ normalize(this.psi);
218
+ }
219
+
220
+ /**
221
+ * Excite multiple rungs uniformly
222
+ * @param {number[]} rungs - Array of rung indices
223
+ * @param {number} [ampScale=1] - Amplitude scale
224
+ */
225
+ exciteRungs(rungs, ampScale = 1) {
226
+ for (const n of rungs) {
227
+ const idx = ((n % this.N) + this.N) % this.N;
228
+ const i = idx * this.d;
229
+ this.psi[i] = C.add(this.psi[i], new C(ampScale, 0));
230
+ }
231
+ normalize(this.psi);
232
+ }
233
+
234
+ /**
235
+ * Excite with prime-based rung mapping
236
+ * @param {number[]} primes - Prime numbers to map to rungs
237
+ * @param {number} [ampScale=1] - Amplitude scale
238
+ */
239
+ excitePrimes(primes, ampScale = 1) {
240
+ for (const p of primes) {
241
+ const idx = ((p % this.N) + this.N) % this.N;
242
+ const i = idx * this.d;
243
+ this.psi[i] = C.add(this.psi[i], new C(ampScale, 0));
244
+ }
245
+ normalize(this.psi);
246
+ }
247
+
248
+ /**
249
+ * Shaped pulse excitation (Gaussian envelope)
250
+ * @param {number} centerRung - Center of the pulse
251
+ * @param {number} width - Width parameter σ
252
+ * @param {number} [amp=1] - Peak amplitude
253
+ * @param {number} [phase=0] - Phase offset
254
+ */
255
+ gaussianPulse(centerRung, width, amp = 1, phase = 0) {
256
+ const sigma = width;
257
+
258
+ for (let n = 0; n < this.N; n++) {
259
+ const dist = Math.min(
260
+ Math.abs(n - centerRung),
261
+ Math.abs(n - centerRung + this.N),
262
+ Math.abs(n - centerRung - this.N)
263
+ );
264
+
265
+ const envelope = amp * Math.exp(-dist * dist / (2 * sigma * sigma));
266
+ const i = n * this.d;
267
+
268
+ this.psi[i] = C.add(this.psi[i], C.mul(
269
+ new C(envelope, 0),
270
+ C.exp(phase)
271
+ ));
272
+ }
273
+
274
+ normalize(this.psi);
275
+ }
276
+
277
+ /**
278
+ * Pi-pulse excitation (flip between states)
279
+ * @param {number} n - Rung to flip
280
+ * @param {number} [phase=0] - Phase of the flip
281
+ */
282
+ piPulse(n, phase = 0) {
283
+ const idx = ((n % this.N) + this.N) % this.N;
284
+ const i = idx * this.d;
285
+
286
+ // Apply π rotation: multiply by e^(iπ/2) = i
287
+ this.psi[i] = C.mul(this.psi[i], C.exp(Math.PI / 2 + phase));
288
+ normalize(this.psi);
289
+ }
290
+
291
+ /**
292
+ * One time step with multi-channel Z dynamics
293
+ * @param {number} [dt=0.01] - Time step size
294
+ */
295
+ step(dt = 0.01) {
296
+ const N = this.N;
297
+ const d = this.d;
298
+ const J = this.getCurrentJ();
299
+
300
+ // --- 1) Core hopping dynamics ---
301
+ const next = Array.from({ length: N * d }, () => C.zero());
302
+
303
+ for (let n = 0; n < N; n++) {
304
+ const nL = (n === 0) ? (this.periodic ? N - 1 : 0) : n - 1;
305
+ const nR = (n === N - 1) ? (this.periodic ? 0 : N - 1) : n + 1;
306
+
307
+ for (let k = 0; k < d; k++) {
308
+ const i = n * d + k;
309
+ const iL = nL * d + k;
310
+ const iR = nR * d + k;
311
+
312
+ const psi = this.psi[i];
313
+ const psiL = this.psi[iL];
314
+ const psiR = this.psi[iR];
315
+
316
+ // Discrete Laplacian
317
+ const lap = C.add(C.sub(psiL, psi), C.sub(psiR, psi));
318
+
319
+ // -i * (dt*J) * lap
320
+ const delta = C.mul(lap, new C(0, -dt * J));
321
+ next[i] = C.add(psi, delta);
322
+ }
323
+ }
324
+
325
+ this.psi = next;
326
+
327
+ // --- 2) Multi-channel leakage ---
328
+ for (const channel of this.channels.values()) {
329
+ channel.lastFlux = 0;
330
+
331
+ for (let i = 0; i < this.psi.length; i++) {
332
+ const a = this.psi[i];
333
+ const moved = C.scale(a, channel.leak);
334
+
335
+ // Subtract from core
336
+ this.psi[i] = C.sub(a, moved);
337
+
338
+ // Add to channel (fold core index into channel index)
339
+ const zi = i % (N * channel.dz);
340
+ channel.z[zi] = C.add(channel.z[zi], moved);
341
+
342
+ channel.lastFlux += C.abs2(moved);
343
+ }
344
+
345
+ channel.totalFlux += channel.lastFlux;
346
+
347
+ // --- 3) Channel internal decay ---
348
+ if (channel.decay > 0) {
349
+ const decayFactor = 1 - channel.decay * dt;
350
+ for (let i = 0; i < channel.z.length; i++) {
351
+ channel.z[i] = C.scale(channel.z[i], decayFactor);
352
+ }
353
+ }
354
+ }
355
+
356
+ // --- 4) Cross-channel coupling ---
357
+ this._applyCrossChannelCoupling(dt);
358
+
359
+ // --- 5) Normalize core ---
360
+ normalize(this.psi);
361
+
362
+ this.t += dt;
363
+ this.stepCount++;
364
+
365
+ return this.metrics();
366
+ }
367
+
368
+ /**
369
+ * Apply cross-channel coupling
370
+ * @private
371
+ */
372
+ _applyCrossChannelCoupling(dt) {
373
+ const channelList = Array.from(this.channels.values());
374
+
375
+ for (let a = 0; a < channelList.length; a++) {
376
+ for (let b = a + 1; b < channelList.length; b++) {
377
+ const chanA = channelList[a];
378
+ const chanB = channelList[b];
379
+
380
+ const coupling = Math.min(chanA.crossCoupling, chanB.crossCoupling);
381
+ if (coupling <= 0) continue;
382
+
383
+ // Transfer some amplitude between channels
384
+ const minLen = Math.min(chanA.z.length, chanB.z.length);
385
+
386
+ for (let i = 0; i < minLen; i++) {
387
+ const zA = chanA.z[i];
388
+ const zB = chanB.z[i];
389
+
390
+ const transfer = coupling * dt;
391
+
392
+ // A -> B
393
+ const toB = C.scale(zA, transfer);
394
+ chanA.z[i] = C.sub(zA, toB);
395
+ chanB.z[i] = C.add(zB, toB);
396
+ }
397
+ }
398
+ }
399
+ }
400
+
401
+ /**
402
+ * Run multiple steps
403
+ * @param {number} steps - Number of steps
404
+ * @param {number} [dt=0.01] - Time step size
405
+ */
406
+ run(steps, dt = 0.01) {
407
+ const trajectory = [];
408
+ for (let i = 0; i < steps; i++) {
409
+ trajectory.push(this.step(dt));
410
+ }
411
+ return trajectory;
412
+ }
413
+
414
+ /**
415
+ * Compute core metrics
416
+ */
417
+ coreMetrics() {
418
+ const p = probsOf(this.psi);
419
+ const H = shannonEntropyNats(p);
420
+
421
+ let meanRe = 0, meanIm = 0;
422
+ for (const v of this.psi) {
423
+ meanRe += v.re;
424
+ meanIm += v.im;
425
+ }
426
+ const orderParameter = Math.sqrt(meanRe * meanRe + meanIm * meanIm) / this.psi.length;
427
+
428
+ return {
429
+ entropy: H,
430
+ coherence: 1 / (1 + H),
431
+ orderParameter
432
+ };
433
+ }
434
+
435
+ /**
436
+ * Compute combined metrics
437
+ */
438
+ metrics() {
439
+ const core = this.coreMetrics();
440
+
441
+ const channelMetrics = {};
442
+ for (const [name, channel] of this.channels) {
443
+ channelMetrics[name] = channel.metrics();
444
+ }
445
+
446
+ // Compute total Z entropy
447
+ let totalZEntropy = 0;
448
+ for (const m of Object.values(channelMetrics)) {
449
+ totalZEntropy += m.entropy;
450
+ }
451
+
452
+ return {
453
+ t: this.t,
454
+ stepCount: this.stepCount,
455
+ core,
456
+ channels: channelMetrics,
457
+ totalZEntropy,
458
+ currentJ: this.getCurrentJ()
459
+ };
460
+ }
461
+
462
+ /**
463
+ * Get per-channel metrics
464
+ */
465
+ channelMetrics() {
466
+ const result = {};
467
+ for (const [name, channel] of this.channels) {
468
+ result[name] = channel.metrics();
469
+ }
470
+ return result;
471
+ }
472
+
473
+ /**
474
+ * Get probability distribution over rungs
475
+ */
476
+ rungProbabilities() {
477
+ const probs = new Array(this.N).fill(0);
478
+ for (let n = 0; n < this.N; n++) {
479
+ for (let k = 0; k < this.d; k++) {
480
+ const i = n * this.d + k;
481
+ probs[n] += C.abs2(this.psi[i]);
482
+ }
483
+ }
484
+ const total = probs.reduce((a, b) => a + b, 0) || 1;
485
+ return probs.map(p => p / total);
486
+ }
487
+
488
+ /**
489
+ * Sample a rung according to |ψ|² distribution
490
+ */
491
+ sampleRung() {
492
+ const probs = this.rungProbabilities();
493
+ const r = Math.random();
494
+ let cumulative = 0;
495
+ for (let n = 0; n < this.N; n++) {
496
+ cumulative += probs[n];
497
+ if (r <= cumulative) return n;
498
+ }
499
+ return this.N - 1;
500
+ }
501
+
502
+ /**
503
+ * Collapse to a specific rung
504
+ * @param {number} n - Rung to collapse to
505
+ */
506
+ collapseToRung(n) {
507
+ const idx = ((n % this.N) + this.N) % this.N;
508
+
509
+ for (let i = 0; i < this.psi.length; i++) {
510
+ const rungIdx = Math.floor(i / this.d);
511
+ if (rungIdx !== idx) {
512
+ this.psi[i] = C.zero();
513
+ }
514
+ }
515
+ normalize(this.psi);
516
+ }
517
+
518
+ /**
519
+ * Perform measurement and collapse
520
+ */
521
+ measure() {
522
+ const probsBefore = this.rungProbabilities();
523
+ const sampledRung = this.sampleRung();
524
+ const probability = probsBefore[sampledRung];
525
+
526
+ this.collapseToRung(sampledRung);
527
+
528
+ return {
529
+ outcome: sampledRung,
530
+ probability,
531
+ probsBefore,
532
+ metricsAfter: this.metrics()
533
+ };
534
+ }
535
+
536
+ /**
537
+ * Compute entanglement entropy between core and Z channels
538
+ * Uses bipartite entanglement measure
539
+ */
540
+ entanglementEntropy() {
541
+ // Compute reduced density matrix for core
542
+ // For simplicity, use purity-based estimate: S ≈ log(d) - log(purity)
543
+
544
+ let corePurity = 0;
545
+ const coreProbs = this.rungProbabilities();
546
+ for (const p of coreProbs) {
547
+ corePurity += p * p;
548
+ }
549
+
550
+ // Von Neumann entropy estimate
551
+ const maxEntropy = Math.log(this.N);
552
+ const entropy = maxEntropy - Math.log(1 / corePurity);
553
+
554
+ return Math.max(0, Math.min(maxEntropy, entropy));
555
+ }
556
+
557
+ /**
558
+ * Get full state snapshot
559
+ */
560
+ snapshot() {
561
+ const channelSnapshots = {};
562
+ for (const [name, channel] of this.channels) {
563
+ channelSnapshots[name] = channel.snapshot();
564
+ }
565
+
566
+ return {
567
+ t: this.t,
568
+ stepCount: this.stepCount,
569
+ N: this.N,
570
+ d: this.d,
571
+ J: this.J,
572
+ periodic: this.periodic,
573
+ psi: this.psi.map(v => ({ re: v.re, im: v.im })),
574
+ channels: channelSnapshots,
575
+ ...this.metrics()
576
+ };
577
+ }
578
+
579
+ /**
580
+ * Restore from snapshot
581
+ */
582
+ restore(snap) {
583
+ this.t = snap.t;
584
+ this.stepCount = snap.stepCount;
585
+ this.psi = snap.psi.map(v => new C(v.re, v.im));
586
+
587
+ for (const [name, channelSnap] of Object.entries(snap.channels)) {
588
+ const channel = this.channels.get(name);
589
+ if (channel) {
590
+ channel.restore(channelSnap);
591
+ }
592
+ }
593
+ }
594
+ }
595
+
596
+ /**
597
+ * Factory function for creating multi-channel ladder with prime-based config
598
+ * @param {number[]} primes - Prime numbers for initialization
599
+ * @param {object} [opts={}] - Additional options
600
+ */
601
+ function createMultiChannelLadder(primes, opts = {}) {
602
+ const N = opts.N ?? Math.max(16, Math.max(...primes) + 1);
603
+
604
+ const zChannels = opts.zChannels || [
605
+ { name: 'fast', dz: 1, leak: 0.2 },
606
+ { name: 'slow', dz: 1, leak: 0.02, decay: 0.001 },
607
+ { name: 'permanent', dz: 1, leak: 0.001, decay: 0 }
608
+ ];
609
+
610
+ const ladder = new PrimeonZLadderMulti({
611
+ N,
612
+ d: opts.d ?? 1,
613
+ J: opts.J ?? 0.25,
614
+ zChannels,
615
+ periodic: opts.periodic ?? true,
616
+ Jt: opts.Jt ?? null
617
+ });
618
+
619
+ if (primes.length > 0) {
620
+ ladder.excitePrimes(primes, opts.ampScale ?? 1);
621
+ }
622
+
623
+ return ladder;
624
+ }
625
+
626
+ /**
627
+ * Adiabatic protocol helper
628
+ * Creates time-dependent J function for adiabatic quantum computing
629
+ *
630
+ * @param {number} J0 - Initial coupling
631
+ * @param {number} J1 - Final coupling
632
+ * @param {number} T - Total evolution time
633
+ * @param {string} [schedule='linear'] - Schedule type
634
+ */
635
+ function createAdiabaticSchedule(J0, J1, T, schedule = 'linear') {
636
+ switch (schedule) {
637
+ case 'linear':
638
+ return (t) => J0 + (J1 - J0) * Math.min(1, t / T);
639
+
640
+ case 'quadratic':
641
+ return (t) => {
642
+ const s = Math.min(1, t / T);
643
+ return J0 + (J1 - J0) * s * s;
644
+ };
645
+
646
+ case 'sinusoidal':
647
+ return (t) => {
648
+ const s = Math.min(1, t / T);
649
+ return J0 + (J1 - J0) * (1 - Math.cos(Math.PI * s)) / 2;
650
+ };
651
+
652
+ case 'exponential':
653
+ return (t) => {
654
+ const s = Math.min(1, t / T);
655
+ const tau = T / 3; // Characteristic time
656
+ return J0 + (J1 - J0) * (1 - Math.exp(-s * T / tau));
657
+ };
658
+
659
+ default:
660
+ return (t) => J0;
661
+ }
662
+ }
663
+
664
+ module.exports = {
665
+ ZChannel,
666
+ PrimeonZLadderMulti,
667
+ createMultiChannelLadder,
668
+ createAdiabaticSchedule
669
+ };