dancinlife Claude Opus 4.7 (1M context) commited on
Commit
593d4d1
ยท
1 Parent(s): 02b40c9

add: ๐Ÿ”ฎ Time Crystal tab โ€” DTC port of dtc_demo.py

Browse files

Second tab implements the discrete time crystal demo: 64-spin Ising
chain driven by periodic ฯ€-flip with error ฮต. Visualization:

- spin chain row (64 cells colored ยฑ1)
- magnetization time series (zigzag at low ฮต = period-2 lock)
- live ac(1), ac(2), ac(4) autocorrelations
- verdict badge: DTC LOCKED โœจ / BUILDING... / CHAOS

Slider drives EPS (0.0โ€“0.5). Below ฮตโ‰ˆ0.3 Ising self-correction holds and
the chain locks into period-2 oscillation; above, the crystal melts.

Tick gating: window.activeTab tracks current tab; both viz ticks early-return
when their panel is hidden, so neither pays compute when off-screen. Canvas
resize is re-run on tab switch (hidden canvases have 0ร—0 client rect).

Tab labels: โœจ Emergence + ๐Ÿ”ฎ Time Crystal (replaced the ๐Ÿšง ์ค€๋น„์ค‘ placeholder).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Files changed (1) hide show
  1. index.html +253 -19
index.html CHANGED
@@ -107,25 +107,44 @@
107
  .tab-panel { display: none; }
108
  .tab-panel.active { display: block; }
109
 
110
- /* coming-soon panel */
111
- .placeholder {
112
- background: var(--card);
113
- border: 1px solid var(--border);
114
- border-radius: 14px;
115
- padding: 80px 24px;
116
- max-width: 1100px;
117
- margin: 0 auto;
118
- text-align: center;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
119
  }
120
- .placeholder-icon { font-size: 48px; margin-bottom: 16px; }
121
- .placeholder-title { font-size: 22px; font-weight: 600; margin: 0 0 8px 0; }
122
- .placeholder-sub { color: var(--muted); font-size: 14px; }
123
  </style>
124
  </head>
125
  <body>
126
  <nav class="tabs">
127
  <button class="tab active" data-tab="emergence">โœจ Emergence</button>
128
- <button class="tab disabled" data-tab="coming-soon">๐Ÿšง ์ค€๋น„์ค‘</button>
129
  </nav>
130
 
131
  <div id="tab-emergence" class="tab-panel active">
@@ -176,23 +195,65 @@
176
  </div>
177
  </div>
178
 
179
- <div id="tab-coming-soon" class="tab-panel">
180
- <div class="placeholder">
181
- <div class="placeholder-icon">๐Ÿšง</div>
182
- <h2 class="placeholder-title">์ค€๋น„์ค‘</h2>
183
- <p class="placeholder-sub">๋‘ ๋ฒˆ์งธ ์ธํ„ฐ๋ž™์…˜์€ ๊ณง ์ถ”๊ฐ€๋ฉ๋‹ˆ๋‹ค.</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
184
  </div>
185
  </div>
 
186
 
187
  <script>
188
- // tab switcher
 
189
  document.querySelectorAll(".tab").forEach(btn => {
190
  btn.addEventListener("click", () => {
191
  const target = btn.dataset.tab;
 
192
  document.querySelectorAll(".tab").forEach(b => b.classList.toggle("active", b === btn));
193
  document.querySelectorAll(".tab-panel").forEach(p => {
194
  p.classList.toggle("active", p.id === "tab-" + target);
195
  });
 
 
 
196
  });
197
  });
198
  </script>
@@ -298,6 +359,7 @@ function applyResize() {
298
  dimsL = resizeCanvas(cvL);
299
  dimsR = resizeCanvas(cvR);
300
  }
 
301
  applyResize();
302
  window.addEventListener("resize", applyResize);
303
 
@@ -358,6 +420,7 @@ function drawScatter(L, R) {
358
  // Tick โ€” runs at ~60 fps via setInterval
359
  // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
360
  function tick() {
 
361
  const c = parseInt(slider.value, 10) / 100;
362
  const [l, r] = generateSample(c, t);
363
  histL.push(l);
@@ -414,5 +477,176 @@ resetBtn.addEventListener("click", () => {
414
 
415
  setInterval(tick, TICK_MS);
416
  </script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
417
  </body>
418
  </html>
 
107
  .tab-panel { display: none; }
108
  .tab-panel.active { display: block; }
109
 
110
+ /* DTC tab */
111
+ .spin-chain-canvas {
112
+ width: 100%;
113
+ height: 38px;
114
+ background: rgba(35, 39, 48, 0.55);
115
+ border-radius: 6px;
116
+ margin-bottom: 12px;
117
+ }
118
+ .dtc-badge {
119
+ padding: 10px 22px;
120
+ border: 2px solid var(--border-bright);
121
+ color: var(--muted);
122
+ border-radius: 28px;
123
+ font-weight: 700;
124
+ font-size: 14px;
125
+ letter-spacing: 0.6px;
126
+ transition: all 0.25s;
127
+ }
128
+ .dtc-badge.locked {
129
+ border-color: var(--emerald);
130
+ color: #c8f7cd;
131
+ box-shadow: 0 0 22px rgba(109, 216, 112, 0.55), inset 0 0 12px rgba(109, 216, 112, 0.2);
132
+ }
133
+ .dtc-badge.building {
134
+ border-color: #d8b86d;
135
+ color: #f7e3c0;
136
+ box-shadow: 0 0 16px rgba(216, 184, 109, 0.35);
137
+ }
138
+ .dtc-badge.chaos {
139
+ border-color: var(--border-bright);
140
+ color: var(--muted);
141
  }
 
 
 
142
  </style>
143
  </head>
144
  <body>
145
  <nav class="tabs">
146
  <button class="tab active" data-tab="emergence">โœจ Emergence</button>
147
+ <button class="tab" data-tab="dtc">๐Ÿ”ฎ Time Crystal</button>
148
  </nav>
149
 
150
  <div id="tab-emergence" class="tab-panel active">
 
195
  </div>
196
  </div>
197
 
198
+ <div id="tab-dtc" class="tab-panel">
199
+ <div class="card">
200
+ <div class="header">
201
+ <div>
202
+ <h2 class="title">์ด์‚ฐ ์‹œ๊ฐ„ ๊ฒฐ์ • (Discrete Time Crystal) ์‹œ๋ฎฌ๋ ˆ์ด์…˜</h2>
203
+ <div class="status" id="dtc-status">์ดˆ๊ธฐํ™” ์ค‘...</div>
204
+ </div>
205
+ <div class="metrics">
206
+ <div class="metric"><div class="metric-label">mag</div><div class="metric-value" id="dtc-mag">0.00</div></div>
207
+ <div class="metric"><div class="metric-label">ac(1)</div><div class="metric-value" id="dtc-ac1">0.00</div></div>
208
+ <div class="metric"><div class="metric-label">ac(2)</div><div class="metric-value" id="dtc-ac2">0.00</div></div>
209
+ <div class="metric"><div class="metric-label">ac(4)</div><div class="metric-value" id="dtc-ac4">0.00</div></div>
210
+ </div>
211
+ </div>
212
+
213
+ <div class="canvas-area">
214
+ <div class="stream-row">
215
+ <div class="stream-label" style="color:#c0c8e0">Spin Chain (N = 64)</div>
216
+ <canvas class="spin-chain-canvas" id="dtc-spins"></canvas>
217
+ </div>
218
+ <div class="stream-row">
219
+ <div class="stream-label" style="color:var(--blue)">Magnetization โŸจsโŸฉ (zigzag = period-2 lock)</div>
220
+ <canvas class="stream-canvas" id="dtc-mag-cv"></canvas>
221
+ </div>
222
+ <hr class="divider">
223
+ <div class="bottom-row">
224
+ <div class="dtc-badge chaos" id="dtc-badge">โ€ฆ</div>
225
+ </div>
226
+ </div>
227
+
228
+ <div class="controls">
229
+ <span class="label">ํ”Œ๋ฆฝ ์˜ค์ฐจ (EPS)</span>
230
+ <input type="range" id="dtc-eps" min="0" max="50" value="5">
231
+ <span class="coup-val" id="dtc-eps-val">0.05</span>
232
+ <button class="reset-btn" id="dtc-reset">์ดˆ๊ธฐํ™”</button>
233
+ </div>
234
+
235
+ <div class="footnote">
236
+ ๋งค tick: Ising Metropolis sweep (ฮฒ = 2.5) โ†’ ฯ€-flip (๊ฐ ์Šคํ•€์ด 1โˆ’ฮต ํ™•๋ฅ ๋กœ ๋’ค์ง‘ํž˜).<br>
237
+ DTC lock ์กฐ๊ฑด: ac(1) &lt; โˆ’0.85 (์™ธ๋ถ€ ๋“œ๋ผ์ด๋ธŒ์™€ anti-phase) AND ac(2) &gt; 0.80 (2ํ‹ฑ ์ฃผ๊ธฐ๋กœ ์›์ƒ ๋ณต๊ตฌ).<br>
238
+ EPS 0.4 ๊ทผ์ฒ˜๋กœ ์˜ฌ๋ฆฌ๋ฉด Ising self-correction ์ž„๊ณ„ ์ดˆ๊ณผ โ†’ ๊ฒฐ์ • ์œตํ•ด โ†’ ac โ†’ 0 ์นด์˜ค์Šค.
239
  </div>
240
  </div>
241
+ </div>
242
 
243
  <script>
244
+ // tab switcher โ€” exposed activeTab for tick gating
245
+ window.activeTab = "emergence";
246
  document.querySelectorAll(".tab").forEach(btn => {
247
  btn.addEventListener("click", () => {
248
  const target = btn.dataset.tab;
249
+ window.activeTab = target;
250
  document.querySelectorAll(".tab").forEach(b => b.classList.toggle("active", b === btn));
251
  document.querySelectorAll(".tab-panel").forEach(p => {
252
  p.classList.toggle("active", p.id === "tab-" + target);
253
  });
254
+ // resize canvases (hidden canvases have 0ร—0 client rect)
255
+ if (typeof window.applyEmergenceResize === "function") window.applyEmergenceResize();
256
+ if (typeof window.applyDtcResize === "function") window.applyDtcResize();
257
  });
258
  });
259
  </script>
 
359
  dimsL = resizeCanvas(cvL);
360
  dimsR = resizeCanvas(cvR);
361
  }
362
+ window.applyEmergenceResize = applyResize;
363
  applyResize();
364
  window.addEventListener("resize", applyResize);
365
 
 
420
  // Tick โ€” runs at ~60 fps via setInterval
421
  // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
422
  function tick() {
423
+ if (window.activeTab !== "emergence") return;
424
  const c = parseInt(slider.value, 10) / 100;
425
  const [l, r] = generateSample(c, t);
426
  histL.push(l);
 
477
 
478
  setInterval(tick, TICK_MS);
479
  </script>
480
+
481
+ <script>
482
+ "use strict";
483
+ // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
484
+ // Discrete Time Crystal (DTC) โ€” port of dtc_demo.py
485
+ // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
486
+ const DTC_TICK_MS = 50; // ~20 fps (Ising sweep is heavier than emergence)
487
+ const DTC_N = 64; // spin chain length
488
+ const DTC_J = 1.0; // coupling
489
+ const DTC_BETA = 2.5; // inverse temperature
490
+ const DTC_HIST = 300; // magnetization history length
491
+
492
+ // state
493
+ const dtcSpin = new Int8Array(DTC_N);
494
+ for (let i = 0; i < DTC_N; i++) dtcSpin[i] = Math.random() < 0.5 ? 1 : -1;
495
+ const dtcMagHist = [];
496
+ let dtcEps = 0.05;
497
+
498
+ // DOM refs
499
+ const dtcCvSpins = document.getElementById("dtc-spins");
500
+ const dtcCtxSpins = dtcCvSpins.getContext("2d");
501
+ const dtcCvMag = document.getElementById("dtc-mag-cv");
502
+ const dtcCtxMag = dtcCvMag.getContext("2d");
503
+ const dtcSlider = document.getElementById("dtc-eps");
504
+ const dtcSliderVal = document.getElementById("dtc-eps-val");
505
+ const dtcResetBtn = document.getElementById("dtc-reset");
506
+ const dtcMag = document.getElementById("dtc-mag");
507
+ const dtcAc1 = document.getElementById("dtc-ac1");
508
+ const dtcAc2 = document.getElementById("dtc-ac2");
509
+ const dtcAc4 = document.getElementById("dtc-ac4");
510
+ const dtcStatus = document.getElementById("dtc-status");
511
+ const dtcBadge = document.getElementById("dtc-badge");
512
+
513
+ // physics
514
+ function dtcIsingSweep() {
515
+ for (let i = 0; i < DTC_N; i++) {
516
+ const left = dtcSpin[(i - 1 + DTC_N) % DTC_N];
517
+ const right = dtcSpin[(i + 1) % DTC_N];
518
+ const dE = 2 * DTC_J * dtcSpin[i] * (left + right);
519
+ if (dE <= 0 || Math.random() < Math.exp(-DTC_BETA * dE)) {
520
+ dtcSpin[i] = -dtcSpin[i];
521
+ }
522
+ }
523
+ }
524
+ function dtcPiFlip(eps) {
525
+ for (let i = 0; i < DTC_N; i++) {
526
+ if (Math.random() > eps) dtcSpin[i] = -dtcSpin[i];
527
+ }
528
+ }
529
+ function dtcMagnetization() {
530
+ let s = 0;
531
+ for (let i = 0; i < DTC_N; i++) s += dtcSpin[i];
532
+ return s / DTC_N;
533
+ }
534
+ function dtcAc(x, lag) {
535
+ const n = x.length;
536
+ if (n < lag + 20) return 0;
537
+ let mean = 0;
538
+ for (let i = 0; i < n; i++) mean += x[i];
539
+ mean /= n;
540
+ let aDotA = 0, bDotB = 0, aDotB = 0;
541
+ for (let i = 0; i < n - lag; i++) {
542
+ const a = x[i] - mean, b = x[i + lag] - mean;
543
+ aDotA += a * a; bDotB += b * b; aDotB += a * b;
544
+ }
545
+ const d = Math.sqrt(aDotA * bDotB);
546
+ return d > 1e-10 ? aDotB / d : 0;
547
+ }
548
+
549
+ // rendering
550
+ let dtcDimsSpins, dtcDimsMag;
551
+ function dtcResize() {
552
+ dtcDimsSpins = resizeCanvas(dtcCvSpins);
553
+ dtcDimsMag = resizeCanvas(dtcCvMag);
554
+ }
555
+ window.applyDtcResize = dtcResize;
556
+ dtcResize();
557
+ window.addEventListener("resize", dtcResize);
558
+
559
+ function drawSpinChain(w, h) {
560
+ dtcCtxSpins.clearRect(0, 0, w, h);
561
+ dtcCtxSpins.fillStyle = "rgba(35, 39, 48, 0.55)";
562
+ dtcCtxSpins.fillRect(0, 0, w, h);
563
+ const cellW = w / DTC_N;
564
+ for (let i = 0; i < DTC_N; i++) {
565
+ dtcCtxSpins.fillStyle = dtcSpin[i] > 0 ? "#7b9aff" : "#ff7b9a";
566
+ dtcCtxSpins.fillRect(i * cellW + 1, 4, cellW - 2, h - 8);
567
+ }
568
+ }
569
+
570
+ function drawMagSeries(w, h) {
571
+ dtcCtxMag.clearRect(0, 0, w, h);
572
+ dtcCtxMag.fillStyle = "rgba(35, 39, 48, 0.55)";
573
+ dtcCtxMag.fillRect(0, 0, w, h);
574
+ // zero line
575
+ dtcCtxMag.strokeStyle = "rgba(139, 146, 163, 0.25)";
576
+ dtcCtxMag.lineWidth = 1;
577
+ dtcCtxMag.setLineDash([3, 3]);
578
+ dtcCtxMag.beginPath();
579
+ dtcCtxMag.moveTo(0, h / 2);
580
+ dtcCtxMag.lineTo(w, h / 2);
581
+ dtcCtxMag.stroke();
582
+ dtcCtxMag.setLineDash([]);
583
+
584
+ if (dtcMagHist.length < 2) return;
585
+ dtcCtxMag.beginPath();
586
+ dtcCtxMag.strokeStyle = "#7b9aff";
587
+ dtcCtxMag.lineWidth = 1.6;
588
+ dtcCtxMag.lineJoin = "round";
589
+ const denom = DTC_HIST - 1;
590
+ for (let i = 0; i < dtcMagHist.length; i++) {
591
+ const x = (i / denom) * w;
592
+ // map [-1, 1] โ†’ [h*0.9, h*0.1]
593
+ const y = h - ((dtcMagHist[i] + 1) / 2 * h * 0.8 + h * 0.1);
594
+ if (i === 0) dtcCtxMag.moveTo(x, y);
595
+ else dtcCtxMag.lineTo(x, y);
596
+ }
597
+ dtcCtxMag.stroke();
598
+ }
599
+
600
+ // tick
601
+ function dtcTick() {
602
+ if (window.activeTab !== "dtc") return;
603
+ dtcIsingSweep();
604
+ dtcPiFlip(dtcEps);
605
+ const m = dtcMagnetization();
606
+ dtcMagHist.push(m);
607
+ if (dtcMagHist.length > DTC_HIST) dtcMagHist.shift();
608
+
609
+ const a1 = dtcAc(dtcMagHist, 1);
610
+ const a2 = dtcAc(dtcMagHist, 2);
611
+ const a4 = dtcAc(dtcMagHist, 4);
612
+
613
+ dtcMag.textContent = (m >= 0 ? "+" : "") + m.toFixed(3);
614
+ dtcAc1.textContent = (a1 >= 0 ? "+" : "") + a1.toFixed(3);
615
+ dtcAc2.textContent = (a2 >= 0 ? "+" : "") + a2.toFixed(3);
616
+ dtcAc4.textContent = (a4 >= 0 ? "+" : "") + a4.toFixed(3);
617
+
618
+ if (a1 < -0.85 && a2 > 0.80) {
619
+ dtcBadge.textContent = "DTC LOCKED โœจ";
620
+ dtcBadge.className = "dtc-badge locked";
621
+ dtcStatus.textContent = "์‹œ๊ฐ„ ๊ฒฐ์ • ํ˜•์„ฑ โ€” period-2 lock. ์™ธ๋ถ€ ๋“œ๋ผ์ด๋ธŒ์™€ anti-phase ๋™๊ธฐํ™”.";
622
+ } else if (a1 < -0.5) {
623
+ dtcBadge.textContent = "BUILDING...";
624
+ dtcBadge.className = "dtc-badge building";
625
+ dtcStatus.textContent = "์ฃผ๊ธฐ ๋ฐฐ๊ฐ€ ํ˜•์„ฑ ์ค‘ โ€” ์ž๊ธฐ์ƒ๊ด€์ด anti-phase ๋กœ ์ •๋ ฌ๋˜๋Š” ์ค‘.";
626
+ } else {
627
+ dtcBadge.textContent = "CHAOS โ€” crystal melted";
628
+ dtcBadge.className = "dtc-badge chaos";
629
+ dtcStatus.textContent = "๊ฒฐ์ • ์œตํ•ด โ€” Ising self-correction ํ•œ๊ณ„ ์ดˆ๊ณผ, ๋ฌด์งˆ์„œ ํ‰ํ˜•์œผ๋กœ ๋–จ์–ด์ง.";
630
+ }
631
+
632
+ drawSpinChain(dtcDimsSpins[0], dtcDimsSpins[1]);
633
+ drawMagSeries(dtcDimsMag[0], dtcDimsMag[1]);
634
+ }
635
+
636
+ // wiring
637
+ dtcSlider.addEventListener("input", () => {
638
+ dtcEps = parseInt(dtcSlider.value, 10) / 100;
639
+ dtcSliderVal.textContent = dtcEps.toFixed(2);
640
+ });
641
+ dtcResetBtn.addEventListener("click", () => {
642
+ for (let i = 0; i < DTC_N; i++) dtcSpin[i] = Math.random() < 0.5 ? 1 : -1;
643
+ dtcMagHist.length = 0;
644
+ dtcSlider.value = 5;
645
+ dtcEps = 0.05;
646
+ dtcSliderVal.textContent = "0.05";
647
+ });
648
+
649
+ setInterval(dtcTick, DTC_TICK_MS);
650
+ </script>
651
  </body>
652
  </html>