jupyterlab-codex-sidebar 0.1.4 → 0.1.6

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 (153) hide show
  1. package/.claude/settings.local.json +9 -0
  2. package/.github/workflows/unit-tests.yml +27 -0
  3. package/.jupyterlab-playwright.log +0 -0
  4. package/README.md +83 -9
  5. package/docs/images/codex-sidebar-screenshot.png +0 -0
  6. package/jupyterlab_codex/handlers.py +938 -297
  7. package/jupyterlab_codex/labextension/package.json +13 -3
  8. package/jupyterlab_codex/labextension/static/525.224526d045c727069de6.js +2 -0
  9. package/jupyterlab_codex/labextension/static/737.e7de3ad9dd6ded798340.js +1 -0
  10. package/jupyterlab_codex/labextension/static/remoteEntry.6ef5e7167763a316c000.js +1 -0
  11. package/jupyterlab_codex/protocol.py +297 -0
  12. package/jupyterlab_codex/runner.py +58 -15
  13. package/jupyterlab_codex/sessions.py +582 -97
  14. package/lib/codexChat.d.ts +13 -0
  15. package/lib/codexChat.js +2506 -0
  16. package/lib/codexChat.js.map +1 -0
  17. package/lib/codexChatAttachmentDedup.d.ts +10 -0
  18. package/lib/codexChatAttachmentDedup.js +35 -0
  19. package/lib/codexChatAttachmentDedup.js.map +1 -0
  20. package/lib/codexChatAttachmentLimit.d.ts +18 -0
  21. package/lib/codexChatAttachmentLimit.js +50 -0
  22. package/lib/codexChatAttachmentLimit.js.map +1 -0
  23. package/lib/codexChatAttachmentState.d.ts +15 -0
  24. package/lib/codexChatAttachmentState.js +16 -0
  25. package/lib/codexChatAttachmentState.js.map +1 -0
  26. package/lib/codexChatDocumentUtils.d.ts +70 -0
  27. package/lib/codexChatDocumentUtils.js +506 -0
  28. package/lib/codexChatDocumentUtils.js.map +1 -0
  29. package/lib/codexChatFormatting.d.ts +11 -0
  30. package/lib/codexChatFormatting.js +83 -0
  31. package/lib/codexChatFormatting.js.map +1 -0
  32. package/lib/codexChatNotice.d.ts +3 -0
  33. package/lib/codexChatNotice.js +74 -0
  34. package/lib/codexChatNotice.js.map +1 -0
  35. package/lib/codexChatPersistence.d.ts +35 -0
  36. package/lib/codexChatPersistence.js +158 -0
  37. package/lib/codexChatPersistence.js.map +1 -0
  38. package/lib/codexChatPrimitives.d.ts +44 -0
  39. package/lib/codexChatPrimitives.js +156 -0
  40. package/lib/codexChatPrimitives.js.map +1 -0
  41. package/lib/codexChatRender.d.ts +24 -0
  42. package/lib/codexChatRender.js +293 -0
  43. package/lib/codexChatRender.js.map +1 -0
  44. package/lib/codexChatSessionFactory.d.ts +15 -0
  45. package/lib/codexChatSessionFactory.js +45 -0
  46. package/lib/codexChatSessionFactory.js.map +1 -0
  47. package/lib/codexChatSessionKey.d.ts +3 -0
  48. package/lib/codexChatSessionKey.js +14 -0
  49. package/lib/codexChatSessionKey.js.map +1 -0
  50. package/lib/codexChatStorage.d.ts +4 -0
  51. package/lib/codexChatStorage.js +37 -0
  52. package/lib/codexChatStorage.js.map +1 -0
  53. package/lib/codexSessionResolver.d.ts +12 -0
  54. package/lib/codexSessionResolver.js +38 -0
  55. package/lib/codexSessionResolver.js.map +1 -0
  56. package/lib/handlers/activitySummarizer.d.ts +15 -0
  57. package/lib/handlers/activitySummarizer.js +327 -0
  58. package/lib/handlers/activitySummarizer.js.map +1 -0
  59. package/lib/handlers/codexMessageTypes.d.ts +30 -0
  60. package/lib/handlers/codexMessageTypes.js +2 -0
  61. package/lib/handlers/codexMessageTypes.js.map +1 -0
  62. package/lib/handlers/codexMessageUtils.d.ts +46 -0
  63. package/lib/handlers/codexMessageUtils.js +144 -0
  64. package/lib/handlers/codexMessageUtils.js.map +1 -0
  65. package/lib/handlers/handleCodexSocketMessage.d.ts +107 -0
  66. package/lib/handlers/handleCodexSocketMessage.js +78 -0
  67. package/lib/handlers/handleCodexSocketMessage.js.map +1 -0
  68. package/lib/handlers/sessionSyncHandler.d.ts +34 -0
  69. package/lib/handlers/sessionSyncHandler.js +181 -0
  70. package/lib/handlers/sessionSyncHandler.js.map +1 -0
  71. package/lib/hooks/useCodexSocket.d.ts +15 -0
  72. package/lib/hooks/useCodexSocket.js +84 -0
  73. package/lib/hooks/useCodexSocket.js.map +1 -0
  74. package/lib/index.js +1 -1
  75. package/lib/index.js.map +1 -1
  76. package/lib/panel.d.ts +1 -11
  77. package/lib/panel.js +1 -2815
  78. package/lib/panel.js.map +1 -1
  79. package/lib/protocol.d.ts +235 -0
  80. package/lib/protocol.js +278 -0
  81. package/lib/protocol.js.map +1 -0
  82. package/package.json +13 -3
  83. package/playwright.config.cjs +27 -0
  84. package/playwright.unit.config.cjs +19 -0
  85. package/pyproject.toml +1 -1
  86. package/release.sh +52 -14
  87. package/scripts/run_playwright_e2e.sh +96 -0
  88. package/scripts/run_playwright_freeze_repro.sh +58 -0
  89. package/scripts/run_playwright_queue_repro.sh +60 -0
  90. package/scripts/run_playwright_repro.sh +55 -0
  91. package/src/codexChat.tsx +3914 -0
  92. package/src/codexChatAttachmentDedup.ts +47 -0
  93. package/src/codexChatAttachmentLimit.ts +81 -0
  94. package/src/codexChatAttachmentState.ts +37 -0
  95. package/src/codexChatDocumentUtils.ts +644 -0
  96. package/src/codexChatFormatting.ts +94 -0
  97. package/src/codexChatNotice.ts +95 -0
  98. package/src/codexChatPersistence.ts +191 -0
  99. package/src/codexChatPrimitives.tsx +446 -0
  100. package/src/codexChatRender.tsx +376 -0
  101. package/src/codexChatSessionFactory.ts +79 -0
  102. package/src/codexChatSessionKey.ts +16 -0
  103. package/src/codexChatStorage.ts +36 -0
  104. package/src/codexSessionResolver.ts +56 -0
  105. package/src/handlers/activitySummarizer.ts +369 -0
  106. package/src/handlers/codexMessageTypes.ts +34 -0
  107. package/src/handlers/codexMessageUtils.ts +217 -0
  108. package/src/handlers/handleCodexSocketMessage.ts +204 -0
  109. package/src/handlers/sessionSyncHandler.ts +308 -0
  110. package/src/hooks/useCodexSocket.ts +109 -0
  111. package/src/index.ts +1 -1
  112. package/src/panel.tsx +1 -4184
  113. package/src/protocol.ts +582 -0
  114. package/style/index.css +480 -11
  115. package/test-results/.last-run.json +4 -0
  116. package/test.py +0 -0
  117. package/tests/e2e/cell-output-error-tail.spec.js +156 -0
  118. package/tests/e2e/codex-ui-test-helpers.js +138 -0
  119. package/tests/e2e/fixtures/notebooks/error-output-tail.ipynb +58 -0
  120. package/tests/e2e/fixtures/notebooks/error-output-tail.py +19 -0
  121. package/tests/e2e/fixtures/notebooks/tab1.ipynb +322 -0
  122. package/tests/e2e/fixtures/notebooks/tab1.py +272 -0
  123. package/tests/e2e/fixtures/notebooks/tab2.ipynb +252 -0
  124. package/tests/e2e/fixtures/notebooks/tab2.py +231 -0
  125. package/tests/e2e/fixtures/notebooks/tab3.ipynb +403 -0
  126. package/tests/e2e/fixtures/notebooks/tab3.py +331 -0
  127. package/tests/e2e/fixtures/notebooks/tab4.py +339 -0
  128. package/tests/e2e/freeze-notebook-tabs-repro.spec.js +295 -0
  129. package/tests/e2e/mock-codex-cli-flood.py +127 -0
  130. package/tests/e2e/mock-codex-cli-prompt-echo.py +88 -0
  131. package/tests/e2e/mock-codex-cli.py +95 -0
  132. package/tests/e2e/queue-multitab-repro.spec.js +189 -0
  133. package/tests/test_handlers.py +116 -0
  134. package/tests/test_protocol.py +169 -0
  135. package/tests/test_session_store_limits.py +50 -0
  136. package/tests/unit/codexChatAttachmentDedup.spec.ts +56 -0
  137. package/tests/unit/codexChatAttachmentLimit.spec.ts +57 -0
  138. package/tests/unit/codexChatAttachmentState.spec.ts +71 -0
  139. package/tests/unit/codexChatDocumentUtils.spec.ts +63 -0
  140. package/tests/unit/codexChatLimit.spec.ts +18 -0
  141. package/tests/unit/codexChatNotice.spec.ts +45 -0
  142. package/tests/unit/codexChatPersistence.spec.ts +199 -0
  143. package/tests/unit/codexChatSessionFactory.spec.ts +94 -0
  144. package/tests/unit/codexChatSessionKey.spec.ts +18 -0
  145. package/tests/unit/codexMessageUtils.spec.ts +89 -0
  146. package/tests/unit/codexSessionResolver.spec.ts +92 -0
  147. package/tests/unit/handleCodexSocketMessage.spec.ts +476 -0
  148. package/tsconfig.tsbuildinfo +1 -1
  149. package/webpack.config.js +6 -0
  150. package/jupyterlab_codex/labextension/static/504.335f3447c84ba3d74517.js +0 -2
  151. package/jupyterlab_codex/labextension/static/972.8e856719e40acc1ef4cb.js +0 -1
  152. package/jupyterlab_codex/labextension/static/remoteEntry.a2982f776a1f0f515640.js +0 -1
  153. /package/jupyterlab_codex/labextension/static/{504.335f3447c84ba3d74517.js.LICENSE.txt → 525.224526d045c727069de6.js.LICENSE.txt} +0 -0
@@ -0,0 +1,322 @@
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "code",
5
+ "execution_count": null,
6
+ "id": "tab1-cell",
7
+ "metadata": {},
8
+ "outputs": [],
9
+ "source": [
10
+ "import os\n",
11
+ "import tempfile\n",
12
+ "\n",
13
+ "import numpy as np\n",
14
+ "\n",
15
+ "# Use a writable cache path in restricted/sandboxed environments.\n",
16
+ "os.environ.setdefault(\"MPLCONFIGDIR\", os.path.join(tempfile.gettempdir(), \"matplotlib\"))\n",
17
+ "os.environ.setdefault(\"XDG_CACHE_HOME\", tempfile.gettempdir())\n",
18
+ "\n",
19
+ "import matplotlib.pyplot as plt"
20
+ ]
21
+ },
22
+ {
23
+ "cell_type": "code",
24
+ "execution_count": null,
25
+ "id": "8f89a000",
26
+ "metadata": {},
27
+ "outputs": [],
28
+ "source": [
29
+ "SCATTERING_PARAMS = {\n",
30
+ " \"nx\": 1024,\n",
31
+ " \"x_min\": -200.0,\n",
32
+ " \"x_max\": 200.0,\n",
33
+ " \"hbar\": 1.0,\n",
34
+ " \"mass\": 1.0,\n",
35
+ " \"dt\": 0.03,\n",
36
+ " \"n_steps\": 1400,\n",
37
+ " \"save_every\": 8,\n",
38
+ " \"barrier_height\": 1.0,\n",
39
+ " \"barrier_width\": 8.0,\n",
40
+ " \"x0\": -90.0,\n",
41
+ " \"sigma\": 10.0,\n",
42
+ " \"k0\": 1.3,\n",
43
+ "}"
44
+ ]
45
+ },
46
+ {
47
+ "cell_type": "code",
48
+ "execution_count": null,
49
+ "id": "b308cd0d",
50
+ "metadata": {},
51
+ "outputs": [],
52
+ "source": [
53
+ "def simulate_1d_scattering(\n",
54
+ " nx=SCATTERING_PARAMS[\"nx\"],\n",
55
+ " x_min=SCATTERING_PARAMS[\"x_min\"],\n",
56
+ " x_max=SCATTERING_PARAMS[\"x_max\"],\n",
57
+ " hbar=SCATTERING_PARAMS[\"hbar\"],\n",
58
+ " mass=SCATTERING_PARAMS[\"mass\"],\n",
59
+ " dt=SCATTERING_PARAMS[\"dt\"],\n",
60
+ " n_steps=SCATTERING_PARAMS[\"n_steps\"],\n",
61
+ " save_every=SCATTERING_PARAMS[\"save_every\"],\n",
62
+ " barrier_height=SCATTERING_PARAMS[\"barrier_height\"],\n",
63
+ " barrier_width=SCATTERING_PARAMS[\"barrier_width\"],\n",
64
+ " x0=SCATTERING_PARAMS[\"x0\"],\n",
65
+ " sigma=SCATTERING_PARAMS[\"sigma\"],\n",
66
+ " k0=SCATTERING_PARAMS[\"k0\"],\n",
67
+ "):\n",
68
+ " \"\"\"분할 단계 FFT를 이용한 1차원 시간의존 슈뢰딩거 방정식 산란 시뮬레이션.\"\"\"\n",
69
+ " if not isinstance(nx, int):\n",
70
+ " raise TypeError(\"nx must be an integer\")\n",
71
+ " if not isinstance(n_steps, int):\n",
72
+ " raise TypeError(\"n_steps must be an integer\")\n",
73
+ " if not isinstance(save_every, int):\n",
74
+ " raise TypeError(\"save_every must be an integer\")\n",
75
+ " if nx < 8:\n",
76
+ " raise ValueError(\"nx must be >= 8\")\n",
77
+ " if x_max <= x_min:\n",
78
+ " raise ValueError(\"x_max must be greater than x_min\")\n",
79
+ " if hbar <= 0 or mass <= 0:\n",
80
+ " raise ValueError(\"hbar and mass must be positive\")\n",
81
+ " if dt <= 0 or n_steps <= 0 or save_every <= 0:\n",
82
+ " raise ValueError(\"dt, n_steps, and save_every must be positive\")\n",
83
+ " if sigma <= 0:\n",
84
+ " raise ValueError(\"sigma must be positive\")\n",
85
+ " if barrier_width < 0:\n",
86
+ " raise ValueError(\"barrier_width must be non-negative\")\n",
87
+ "\n",
88
+ " # 실공간 격자(x), 파수공간 격자(k) 생성\n",
89
+ " x = np.linspace(x_min, x_max, nx, endpoint=False)\n",
90
+ " dx = x[1] - x[0]\n",
91
+ " k = 2.0 * np.pi * np.fft.fftfreq(nx, d=dx)\n",
92
+ "\n",
93
+ " # 중앙 사각 장벽 퍼텐셜\n",
94
+ " V = np.where(np.abs(x) < barrier_width / 2.0, barrier_height, 0.0)\n",
95
+ "\n",
96
+ " psi = np.exp(-((x - x0) ** 2) / (4.0 * sigma**2)) * np.exp(1j * k0 * x)\n",
97
+ " psi /= np.sqrt(np.sum(np.abs(psi) ** 2) * dx)\n",
98
+ " norm_initial = 1.0\n",
99
+ "\n",
100
+ " # 분할 연산자(퍼텐셜 반 스텝 + 운동에너지 한 스텝)\n",
101
+ " exp_v_half = np.exp(-1j * V * dt / (2.0 * hbar))\n",
102
+ " exp_t = np.exp(-1j * (hbar * k**2 / (2.0 * mass)) * dt)\n",
103
+ "\n",
104
+ " snapshots = []\n",
105
+ " times = []\n",
106
+ "\n",
107
+ " for step in range(n_steps):\n",
108
+ " psi = exp_v_half * psi\n",
109
+ " psi_k = np.fft.fft(psi)\n",
110
+ " psi_k *= exp_t\n",
111
+ " psi = np.fft.ifft(psi_k)\n",
112
+ " psi = exp_v_half * psi\n",
113
+ "\n",
114
+ " # 일정 간격으로 확률밀도 스냅샷 저장\n",
115
+ " if step % save_every == 0:\n",
116
+ " density = np.abs(psi) ** 2\n",
117
+ " snapshots.append(density)\n",
118
+ " times.append((step + 1) * dt)\n",
119
+ "\n",
120
+ " # 좌/중앙/우 영역별 확률 계산\n",
121
+ " left_region = x < -barrier_width / 2.0\n",
122
+ " right_region = x > barrier_width / 2.0\n",
123
+ " center_region = ~(left_region | right_region)\n",
124
+ "\n",
125
+ " density_final = np.abs(psi) ** 2\n",
126
+ " norm_final = np.sum(density_final) * dx\n",
127
+ " reflection = np.sum(density_final[left_region]) * dx / norm_final\n",
128
+ " transmission = np.sum(density_final[right_region]) * dx / norm_final\n",
129
+ " center_probability = np.sum(density_final[center_region]) * dx / norm_final\n",
130
+ "\n",
131
+ " return {\n",
132
+ " \"x\": x,\n",
133
+ " \"V\": V,\n",
134
+ " \"density_map\": np.array(snapshots),\n",
135
+ " \"times\": np.array(times),\n",
136
+ " \"reflection\": reflection,\n",
137
+ " \"transmission\": transmission,\n",
138
+ " \"center_probability\": center_probability,\n",
139
+ " \"probability_balance\": reflection + transmission + center_probability,\n",
140
+ " \"norm_drift\": abs(norm_final - norm_initial),\n",
141
+ " \"norm_final\": norm_final,\n",
142
+ " }\n",
143
+ "\n",
144
+ "\n",
145
+ "def validate_scattering_result(result, params, norm_tol=1e-8, prob_tol=5e-3):\n",
146
+ " # 결과 배열 형태, 시간 증가성, 확률 보존 성질 검증\n",
147
+ " density_map = result[\"density_map\"]\n",
148
+ " times = result[\"times\"]\n",
149
+ " nx = params[\"nx\"]\n",
150
+ " n_steps = params[\"n_steps\"]\n",
151
+ " save_every = params[\"save_every\"]\n",
152
+ "\n",
153
+ " expected_n_snapshots = (n_steps - 1) // save_every + 1\n",
154
+ " if density_map.shape != (expected_n_snapshots, nx):\n",
155
+ " raise ValueError(\n",
156
+ " f\"density_map shape mismatch: expected {(expected_n_snapshots, nx)}, got {density_map.shape}\"\n",
157
+ " )\n",
158
+ " if times.shape != (expected_n_snapshots,):\n",
159
+ " raise ValueError(\n",
160
+ " f\"times shape mismatch: expected {(expected_n_snapshots,)}, got {times.shape}\"\n",
161
+ " )\n",
162
+ " if np.any(density_map < -1e-14):\n",
163
+ " raise ValueError(\"Density has negative values below numerical tolerance.\")\n",
164
+ " if not np.all(np.diff(times) > 0):\n",
165
+ " raise ValueError(\"times must be strictly increasing.\")\n",
166
+ " if result[\"norm_drift\"] > norm_tol:\n",
167
+ " raise ValueError(f\"Norm drift is too large: {result['norm_drift']:.3e}\")\n",
168
+ " if abs(result[\"probability_balance\"] - 1.0) > prob_tol:\n",
169
+ " raise ValueError(\n",
170
+ " \"Reflection + Transmission + Center probability is out of range: \"\n",
171
+ " f\"{result['probability_balance']:.6f}\"\n",
172
+ " )\n",
173
+ "\n",
174
+ "\n",
175
+ "result = simulate_1d_scattering(**SCATTERING_PARAMS)\n",
176
+ "print(f\"Reflection ~ {result['reflection']:.3f}\")\n",
177
+ "print(f\"Transmission ~ {result['transmission']:.3f}\")\n",
178
+ "print(f\"Center prob. ~ {result['center_probability']:.3f}\")\n",
179
+ "print(f\"R + T + C ~ {result['probability_balance']:.3f}\")\n",
180
+ "print(f\"Norm drift ~ {result['norm_drift']:.2e}\")\n",
181
+ "validate_scattering_result(result, SCATTERING_PARAMS)\n",
182
+ "print(\"Validation passed: norm, shape, and probability checks are within tolerance.\")"
183
+ ]
184
+ },
185
+ {
186
+ "cell_type": "code",
187
+ "execution_count": null,
188
+ "id": "17ccc384",
189
+ "metadata": {
190
+ "lines_to_next_cell": 1
191
+ },
192
+ "outputs": [],
193
+ "source": [
194
+ "# 1D scattering visualization\n",
195
+ "x = result[\"x\"]\n",
196
+ "V = result[\"V\"]\n",
197
+ "density_map = result[\"density_map\"]\n",
198
+ "times = result[\"times\"]\n",
199
+ "\n",
200
+ "snapshot_idx = [0, len(density_map) // 3, 2 * len(density_map) // 3, len(density_map) - 1]\n",
201
+ "\n",
202
+ "plt.figure(figsize=(10, 5))\n",
203
+ "for idx in snapshot_idx:\n",
204
+ " plt.plot(x, density_map[idx], label=f\"t = {times[idx]:.2f}\")\n",
205
+ "\n",
206
+ "v_max = np.max(V)\n",
207
+ "v_scale = v_max if v_max > 0 else 1.0\n",
208
+ "plt.plot(x, V / (v_scale * 8.0), \"k--\", alpha=0.7, label=\"Potential (scaled)\")\n",
209
+ "plt.xlim(-150, 120)\n",
210
+ "plt.ylim(bottom=0)\n",
211
+ "plt.xlabel(\"x\")\n",
212
+ "plt.ylabel(r\"$|\\psi(x,t)|^2$\")\n",
213
+ "plt.title(\"Wave Packet Scattering\")\n",
214
+ "plt.legend()\n",
215
+ "plt.tight_layout()\n",
216
+ "plt.show()\n",
217
+ "\n",
218
+ "plt.figure(figsize=(10, 5))\n",
219
+ "extent = [x[0], x[-1], times[-1], times[0]]\n",
220
+ "plt.imshow(density_map, aspect=\"auto\", extent=extent, cmap=\"magma\")\n",
221
+ "plt.colorbar(label=r\"$|\\psi|^2$\")\n",
222
+ "plt.xlabel(\"x\")\n",
223
+ "plt.ylabel(\"time\")\n",
224
+ "plt.title(\"Probability Density Evolution\")\n",
225
+ "plt.tight_layout()\n",
226
+ "plt.show()"
227
+ ]
228
+ },
229
+ {
230
+ "cell_type": "code",
231
+ "execution_count": null,
232
+ "id": "671266d3",
233
+ "metadata": {},
234
+ "outputs": [],
235
+ "source": [
236
+ "def simulate_two_level(delta=1.0, omega=2.0, dt=0.01, n_steps=3000):\n",
237
+ " \"\"\"Two-level system with Hamiltonian H = (delta/2) sz + (omega/2) sx.\"\"\"\n",
238
+ " # 시간 간격과 스텝 수의 기본 유효성 검사\n",
239
+ " if dt <= 0:\n",
240
+ " raise ValueError(\"dt must be positive\")\n",
241
+ " if not isinstance(n_steps, int):\n",
242
+ " raise TypeError(\"n_steps must be an integer\")\n",
243
+ " if n_steps <= 1:\n",
244
+ " raise ValueError(\"n_steps must be > 1\")\n",
245
+ "\n",
246
+ " # 파울리 행렬 정의\n",
247
+ " sigma_x = np.array([[0.0, 1.0], [1.0, 0.0]], dtype=complex)\n",
248
+ " sigma_z = np.array([[1.0, 0.0], [0.0, -1.0]], dtype=complex)\n",
249
+ "\n",
250
+ " # 해밀토니안 고유분해로 한 스텝 진화 연산자 U(dt) 구성\n",
251
+ " H = 0.5 * delta * sigma_z + 0.5 * omega * sigma_x\n",
252
+ " evals, evecs = np.linalg.eigh(H)\n",
253
+ " U_dt = evecs @ np.diag(np.exp(-1j * evals * dt)) @ evecs.conj().T\n",
254
+ "\n",
255
+ " # 초기 상태 |0>에서 시작하여 각 시간의 점유 확률 저장\n",
256
+ " psi = np.array([1.0 + 0j, 0.0 + 0j])\n",
257
+ " t = np.arange(n_steps) * dt\n",
258
+ " p0 = np.empty(n_steps)\n",
259
+ " p1 = np.empty(n_steps)\n",
260
+ "\n",
261
+ " for i in range(n_steps):\n",
262
+ " p0[i] = np.abs(psi[0]) ** 2\n",
263
+ " p1[i] = np.abs(psi[1]) ** 2\n",
264
+ " psi = U_dt @ psi\n",
265
+ "\n",
266
+ " return t, p0, p1\n",
267
+ "\n",
268
+ "\n",
269
+ "def validate_two_level_result(t, p0, p1, tol=1e-10):\n",
270
+ " # 배열 차원/길이/물리적 보존량을 점검하는 검증 함수\n",
271
+ " if t.ndim != 1 or p0.ndim != 1 or p1.ndim != 1:\n",
272
+ " raise ValueError(\"t, p0, and p1 must be 1D arrays\")\n",
273
+ " if not (len(t) == len(p0) == len(p1)):\n",
274
+ " raise ValueError(\"t, p0, and p1 must have the same length\")\n",
275
+ " if np.any(p0 < -tol) or np.any(p1 < -tol):\n",
276
+ " raise ValueError(\"Population contains negative values below tolerance\")\n",
277
+ " if not np.all(np.diff(t) > 0):\n",
278
+ " raise ValueError(\"time values must be strictly increasing\")\n",
279
+ " if np.max(np.abs((p0 + p1) - 1.0)) > tol:\n",
280
+ " raise ValueError(\"Population conservation check failed: p0 + p1 != 1\")\n",
281
+ "\n",
282
+ "\n",
283
+ "t2, p0, p1 = simulate_two_level()\n",
284
+ "validate_two_level_result(t2, p0, p1)\n",
285
+ "print(\"Two-level validation passed: shape, monotonic time, and population conservation.\")\n",
286
+ "plt.figure(figsize=(9, 4))\n",
287
+ "plt.plot(t2, p0, label=\"P(|0>)\")\n",
288
+ "plt.plot(t2, p1, label=\"P(|1>)\")\n",
289
+ "plt.xlabel(\"time\")\n",
290
+ "plt.ylabel(\"population\")\n",
291
+ "plt.title(\"Two-Level Quantum Oscillation\")\n",
292
+ "plt.legend()\n",
293
+ "plt.tight_layout()\n",
294
+ "plt.show()"
295
+ ]
296
+ }
297
+ ],
298
+ "metadata": {
299
+ "jupytext": {
300
+ "formats": "ipynb,py:percent"
301
+ },
302
+ "kernelspec": {
303
+ "display_name": "Python 3 (ipykernel)",
304
+ "language": "python",
305
+ "name": "python3"
306
+ },
307
+ "language_info": {
308
+ "codemirror_mode": {
309
+ "name": "ipython",
310
+ "version": 3
311
+ },
312
+ "file_extension": ".py",
313
+ "mimetype": "text/x-python",
314
+ "name": "python",
315
+ "nbconvert_exporter": "python",
316
+ "pygments_lexer": "ipython3",
317
+ "version": "3.13.9"
318
+ }
319
+ },
320
+ "nbformat": 4,
321
+ "nbformat_minor": 5
322
+ }
@@ -0,0 +1,272 @@
1
+ # ---
2
+ # jupyter:
3
+ # jupytext:
4
+ # formats: ipynb,py:percent
5
+ # text_representation:
6
+ # extension: .py
7
+ # format_name: percent
8
+ # format_version: '1.3'
9
+ # jupytext_version: 1.19.1
10
+ # kernelspec:
11
+ # display_name: Python 3 (ipykernel)
12
+ # language: python
13
+ # name: python3
14
+ # ---
15
+
16
+ # %%
17
+ import os
18
+ import tempfile
19
+
20
+ import numpy as np
21
+
22
+ # Use a writable cache path in restricted/sandboxed environments.
23
+ os.environ.setdefault("MPLCONFIGDIR", os.path.join(tempfile.gettempdir(), "matplotlib"))
24
+ os.environ.setdefault("XDG_CACHE_HOME", tempfile.gettempdir())
25
+
26
+ import matplotlib.pyplot as plt
27
+
28
+ # %%
29
+ SCATTERING_PARAMS = {
30
+ "nx": 1024,
31
+ "x_min": -200.0,
32
+ "x_max": 200.0,
33
+ "hbar": 1.0,
34
+ "mass": 1.0,
35
+ "dt": 0.03,
36
+ "n_steps": 1400,
37
+ "save_every": 8,
38
+ "barrier_height": 1.0,
39
+ "barrier_width": 8.0,
40
+ "x0": -90.0,
41
+ "sigma": 10.0,
42
+ "k0": 1.3,
43
+ }
44
+
45
+
46
+ # %%
47
+ def simulate_1d_scattering(
48
+ nx=SCATTERING_PARAMS["nx"],
49
+ x_min=SCATTERING_PARAMS["x_min"],
50
+ x_max=SCATTERING_PARAMS["x_max"],
51
+ hbar=SCATTERING_PARAMS["hbar"],
52
+ mass=SCATTERING_PARAMS["mass"],
53
+ dt=SCATTERING_PARAMS["dt"],
54
+ n_steps=SCATTERING_PARAMS["n_steps"],
55
+ save_every=SCATTERING_PARAMS["save_every"],
56
+ barrier_height=SCATTERING_PARAMS["barrier_height"],
57
+ barrier_width=SCATTERING_PARAMS["barrier_width"],
58
+ x0=SCATTERING_PARAMS["x0"],
59
+ sigma=SCATTERING_PARAMS["sigma"],
60
+ k0=SCATTERING_PARAMS["k0"],
61
+ ):
62
+ """분할 단계 FFT를 이용한 1차원 시간의존 슈뢰딩거 방정식 산란 시뮬레이션."""
63
+ if not isinstance(nx, int):
64
+ raise TypeError("nx must be an integer")
65
+ if not isinstance(n_steps, int):
66
+ raise TypeError("n_steps must be an integer")
67
+ if not isinstance(save_every, int):
68
+ raise TypeError("save_every must be an integer")
69
+ if nx < 8:
70
+ raise ValueError("nx must be >= 8")
71
+ if x_max <= x_min:
72
+ raise ValueError("x_max must be greater than x_min")
73
+ if hbar <= 0 or mass <= 0:
74
+ raise ValueError("hbar and mass must be positive")
75
+ if dt <= 0 or n_steps <= 0 or save_every <= 0:
76
+ raise ValueError("dt, n_steps, and save_every must be positive")
77
+ if sigma <= 0:
78
+ raise ValueError("sigma must be positive")
79
+ if barrier_width < 0:
80
+ raise ValueError("barrier_width must be non-negative")
81
+
82
+ # 실공간 격자(x), 파수공간 격자(k) 생성
83
+ x = np.linspace(x_min, x_max, nx, endpoint=False)
84
+ dx = x[1] - x[0]
85
+ k = 2.0 * np.pi * np.fft.fftfreq(nx, d=dx)
86
+
87
+ # 중앙 사각 장벽 퍼텐셜
88
+ V = np.where(np.abs(x) < barrier_width / 2.0, barrier_height, 0.0)
89
+
90
+ psi = np.exp(-((x - x0) ** 2) / (4.0 * sigma**2)) * np.exp(1j * k0 * x)
91
+ psi /= np.sqrt(np.sum(np.abs(psi) ** 2) * dx)
92
+ norm_initial = 1.0
93
+
94
+ # 분할 연산자(퍼텐셜 반 스텝 + 운동에너지 한 스텝)
95
+ exp_v_half = np.exp(-1j * V * dt / (2.0 * hbar))
96
+ exp_t = np.exp(-1j * (hbar * k**2 / (2.0 * mass)) * dt)
97
+
98
+ snapshots = []
99
+ times = []
100
+
101
+ for step in range(n_steps):
102
+ psi = exp_v_half * psi
103
+ psi_k = np.fft.fft(psi)
104
+ psi_k *= exp_t
105
+ psi = np.fft.ifft(psi_k)
106
+ psi = exp_v_half * psi
107
+
108
+ # 일정 간격으로 확률밀도 스냅샷 저장
109
+ if step % save_every == 0:
110
+ density = np.abs(psi) ** 2
111
+ snapshots.append(density)
112
+ times.append((step + 1) * dt)
113
+
114
+ # 좌/중앙/우 영역별 확률 계산
115
+ left_region = x < -barrier_width / 2.0
116
+ right_region = x > barrier_width / 2.0
117
+ center_region = ~(left_region | right_region)
118
+
119
+ density_final = np.abs(psi) ** 2
120
+ norm_final = np.sum(density_final) * dx
121
+ reflection = np.sum(density_final[left_region]) * dx / norm_final
122
+ transmission = np.sum(density_final[right_region]) * dx / norm_final
123
+ center_probability = np.sum(density_final[center_region]) * dx / norm_final
124
+
125
+ return {
126
+ "x": x,
127
+ "V": V,
128
+ "density_map": np.array(snapshots),
129
+ "times": np.array(times),
130
+ "reflection": reflection,
131
+ "transmission": transmission,
132
+ "center_probability": center_probability,
133
+ "probability_balance": reflection + transmission + center_probability,
134
+ "norm_drift": abs(norm_final - norm_initial),
135
+ "norm_final": norm_final,
136
+ }
137
+
138
+
139
+ def validate_scattering_result(result, params, norm_tol=1e-8, prob_tol=5e-3):
140
+ # 결과 배열 형태, 시간 증가성, 확률 보존 성질 검증
141
+ density_map = result["density_map"]
142
+ times = result["times"]
143
+ nx = params["nx"]
144
+ n_steps = params["n_steps"]
145
+ save_every = params["save_every"]
146
+
147
+ expected_n_snapshots = (n_steps - 1) // save_every + 1
148
+ if density_map.shape != (expected_n_snapshots, nx):
149
+ raise ValueError(
150
+ f"density_map shape mismatch: expected {(expected_n_snapshots, nx)}, got {density_map.shape}"
151
+ )
152
+ if times.shape != (expected_n_snapshots,):
153
+ raise ValueError(
154
+ f"times shape mismatch: expected {(expected_n_snapshots,)}, got {times.shape}"
155
+ )
156
+ if np.any(density_map < -1e-14):
157
+ raise ValueError("Density has negative values below numerical tolerance.")
158
+ if not np.all(np.diff(times) > 0):
159
+ raise ValueError("times must be strictly increasing.")
160
+ if result["norm_drift"] > norm_tol:
161
+ raise ValueError(f"Norm drift is too large: {result['norm_drift']:.3e}")
162
+ if abs(result["probability_balance"] - 1.0) > prob_tol:
163
+ raise ValueError(
164
+ "Reflection + Transmission + Center probability is out of range: "
165
+ f"{result['probability_balance']:.6f}"
166
+ )
167
+
168
+
169
+ result = simulate_1d_scattering(**SCATTERING_PARAMS)
170
+ print(f"Reflection ~ {result['reflection']:.3f}")
171
+ print(f"Transmission ~ {result['transmission']:.3f}")
172
+ print(f"Center prob. ~ {result['center_probability']:.3f}")
173
+ print(f"R + T + C ~ {result['probability_balance']:.3f}")
174
+ print(f"Norm drift ~ {result['norm_drift']:.2e}")
175
+ validate_scattering_result(result, SCATTERING_PARAMS)
176
+ print("Validation passed: norm, shape, and probability checks are within tolerance.")
177
+
178
+ # %%
179
+ # 1D scattering visualization
180
+ x = result["x"]
181
+ V = result["V"]
182
+ density_map = result["density_map"]
183
+ times = result["times"]
184
+
185
+ snapshot_idx = [0, len(density_map) // 3, 2 * len(density_map) // 3, len(density_map) - 1]
186
+
187
+ plt.figure(figsize=(10, 5))
188
+ for idx in snapshot_idx:
189
+ plt.plot(x, density_map[idx], label=f"t = {times[idx]:.2f}")
190
+
191
+ v_max = np.max(V)
192
+ v_scale = v_max if v_max > 0 else 1.0
193
+ plt.plot(x, V / (v_scale * 8.0), "k--", alpha=0.7, label="Potential (scaled)")
194
+ plt.xlim(-150, 120)
195
+ plt.ylim(bottom=0)
196
+ plt.xlabel("x")
197
+ plt.ylabel(r"$|\psi(x,t)|^2$")
198
+ plt.title("Wave Packet Scattering")
199
+ plt.legend()
200
+ plt.tight_layout()
201
+ plt.show()
202
+
203
+ plt.figure(figsize=(10, 5))
204
+ extent = [x[0], x[-1], times[-1], times[0]]
205
+ plt.imshow(density_map, aspect="auto", extent=extent, cmap="magma")
206
+ plt.colorbar(label=r"$|\psi|^2$")
207
+ plt.xlabel("x")
208
+ plt.ylabel("time")
209
+ plt.title("Probability Density Evolution")
210
+ plt.tight_layout()
211
+ plt.show()
212
+
213
+ # %%
214
+ def simulate_two_level(delta=1.0, omega=2.0, dt=0.01, n_steps=3000):
215
+ """Two-level system with Hamiltonian H = (delta/2) sz + (omega/2) sx."""
216
+ # 시간 간격과 스텝 수의 기본 유효성 검사
217
+ if dt <= 0:
218
+ raise ValueError("dt must be positive")
219
+ if not isinstance(n_steps, int):
220
+ raise TypeError("n_steps must be an integer")
221
+ if n_steps <= 1:
222
+ raise ValueError("n_steps must be > 1")
223
+
224
+ # 파울리 행렬 정의
225
+ sigma_x = np.array([[0.0, 1.0], [1.0, 0.0]], dtype=complex)
226
+ sigma_z = np.array([[1.0, 0.0], [0.0, -1.0]], dtype=complex)
227
+
228
+ # 해밀토니안 고유분해로 한 스텝 진화 연산자 U(dt) 구성
229
+ H = 0.5 * delta * sigma_z + 0.5 * omega * sigma_x
230
+ evals, evecs = np.linalg.eigh(H)
231
+ U_dt = evecs @ np.diag(np.exp(-1j * evals * dt)) @ evecs.conj().T
232
+
233
+ # 초기 상태 |0>에서 시작하여 각 시간의 점유 확률 저장
234
+ psi = np.array([1.0 + 0j, 0.0 + 0j])
235
+ t = np.arange(n_steps) * dt
236
+ p0 = np.empty(n_steps)
237
+ p1 = np.empty(n_steps)
238
+
239
+ for i in range(n_steps):
240
+ p0[i] = np.abs(psi[0]) ** 2
241
+ p1[i] = np.abs(psi[1]) ** 2
242
+ psi = U_dt @ psi
243
+
244
+ return t, p0, p1
245
+
246
+
247
+ def validate_two_level_result(t, p0, p1, tol=1e-10):
248
+ # 배열 차원/길이/물리적 보존량을 점검하는 검증 함수
249
+ if t.ndim != 1 or p0.ndim != 1 or p1.ndim != 1:
250
+ raise ValueError("t, p0, and p1 must be 1D arrays")
251
+ if not (len(t) == len(p0) == len(p1)):
252
+ raise ValueError("t, p0, and p1 must have the same length")
253
+ if np.any(p0 < -tol) or np.any(p1 < -tol):
254
+ raise ValueError("Population contains negative values below tolerance")
255
+ if not np.all(np.diff(t) > 0):
256
+ raise ValueError("time values must be strictly increasing")
257
+ if np.max(np.abs((p0 + p1) - 1.0)) > tol:
258
+ raise ValueError("Population conservation check failed: p0 + p1 != 1")
259
+
260
+
261
+ t2, p0, p1 = simulate_two_level()
262
+ validate_two_level_result(t2, p0, p1)
263
+ print("Two-level validation passed: shape, monotonic time, and population conservation.")
264
+ plt.figure(figsize=(9, 4))
265
+ plt.plot(t2, p0, label="P(|0>)")
266
+ plt.plot(t2, p1, label="P(|1>)")
267
+ plt.xlabel("time")
268
+ plt.ylabel("population")
269
+ plt.title("Two-Level Quantum Oscillation")
270
+ plt.legend()
271
+ plt.tight_layout()
272
+ plt.show()