qubasic 0.1.0__py3-none-any.whl
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.
- qbasic.py +113 -0
- qbasic_core/__init__.py +80 -0
- qbasic_core/__main__.py +6 -0
- qbasic_core/analysis.py +179 -0
- qbasic_core/backend.py +76 -0
- qbasic_core/classic.py +419 -0
- qbasic_core/control_flow.py +576 -0
- qbasic_core/debug.py +327 -0
- qbasic_core/demos.py +356 -0
- qbasic_core/display.py +274 -0
- qbasic_core/engine.py +126 -0
- qbasic_core/engine_state.py +109 -0
- qbasic_core/errors.py +37 -0
- qbasic_core/exec_context.py +24 -0
- qbasic_core/executor.py +861 -0
- qbasic_core/expression.py +228 -0
- qbasic_core/file_io.py +457 -0
- qbasic_core/gates.py +284 -0
- qbasic_core/help_text.py +167 -0
- qbasic_core/io_protocol.py +33 -0
- qbasic_core/locc.py +10 -0
- qbasic_core/locc_commands.py +221 -0
- qbasic_core/locc_display.py +61 -0
- qbasic_core/locc_engine.py +195 -0
- qbasic_core/locc_execution.py +389 -0
- qbasic_core/memory.py +369 -0
- qbasic_core/mock_backend.py +66 -0
- qbasic_core/noise_mixin.py +96 -0
- qbasic_core/parser.py +564 -0
- qbasic_core/patterns.py +186 -0
- qbasic_core/profiler.py +156 -0
- qbasic_core/program_mgmt.py +369 -0
- qbasic_core/protocol.py +77 -0
- qbasic_core/py.typed +0 -0
- qbasic_core/scope.py +74 -0
- qbasic_core/screen.py +115 -0
- qbasic_core/state_display.py +60 -0
- qbasic_core/statements.py +387 -0
- qbasic_core/strings.py +107 -0
- qbasic_core/subs.py +261 -0
- qbasic_core/sweep.py +82 -0
- qbasic_core/terminal.py +1697 -0
- qubasic-0.1.0.dist-info/METADATA +736 -0
- qubasic-0.1.0.dist-info/RECORD +48 -0
- qubasic-0.1.0.dist-info/WHEEL +5 -0
- qubasic-0.1.0.dist-info/entry_points.txt +2 -0
- qubasic-0.1.0.dist-info/licenses/LICENSE +21 -0
- qubasic-0.1.0.dist-info/top_level.txt +2 -0
qbasic_core/debug.py
ADDED
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
"""QBASIC debug — ON ERROR, TRON/TROFF, STOP/CONT, breakpoints, watch, ASSERT, callbacks."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import time
|
|
6
|
+
|
|
7
|
+
MAX_SV_CHECKPOINTS = 1000
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from qbasic_core.engine import (
|
|
11
|
+
ExecResult, ExecOutcome,
|
|
12
|
+
RE_ON_ERROR, RE_RESUME, RE_ERROR_STMT, RE_ASSERT,
|
|
13
|
+
RE_ON_MEASURE, RE_ON_TIMER,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class DebugMixin:
|
|
18
|
+
"""Error handling, debugging, and event callbacks for QBasicTerminal.
|
|
19
|
+
|
|
20
|
+
Requires: TerminalProtocol — uses self.program, self.variables,
|
|
21
|
+
self._eval_condition(), self._gosub_stack.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def _init_debug(self) -> None:
|
|
25
|
+
self._error_target: int | None = None
|
|
26
|
+
self._err_code: int = 0
|
|
27
|
+
self._err_line: int = 0
|
|
28
|
+
self._in_error_handler: bool = False
|
|
29
|
+
self._trace_mode: bool = False
|
|
30
|
+
self._stopped_ip: int | None = None
|
|
31
|
+
self._cont_skip_stop_ip: int | None = None
|
|
32
|
+
self._breakpoints: set[int] = set()
|
|
33
|
+
self._watches: list[str] = []
|
|
34
|
+
self._on_measure_target: int | None = None
|
|
35
|
+
self._on_timer_target: int | None = None
|
|
36
|
+
self._on_timer_interval: float = 0.0
|
|
37
|
+
self._on_timer_last: float = 0.0
|
|
38
|
+
# Time-travel: statevector checkpoints indexed by gate count
|
|
39
|
+
self._sv_checkpoints: list[tuple[int, Any]] = [] # [(line_num, sv_copy)]
|
|
40
|
+
self._tt_position: int = -1 # current position in checkpoint list
|
|
41
|
+
|
|
42
|
+
# ── Error handling ─────────────────────────────────────────────────
|
|
43
|
+
|
|
44
|
+
def _cf_on_error(self, stmt: str, *, parsed=None) -> tuple[bool, ExecOutcome] | None:
|
|
45
|
+
if parsed is not None:
|
|
46
|
+
target = parsed.target
|
|
47
|
+
else:
|
|
48
|
+
m = RE_ON_ERROR.match(stmt)
|
|
49
|
+
if not m:
|
|
50
|
+
return None
|
|
51
|
+
target = int(m.group(1))
|
|
52
|
+
if target == 0:
|
|
53
|
+
self._error_target = None
|
|
54
|
+
else:
|
|
55
|
+
self._error_target = target
|
|
56
|
+
return True, ExecResult.ADVANCE
|
|
57
|
+
|
|
58
|
+
def _cf_resume(self, stmt: str, sorted_lines: list[int], *, parsed=None) -> tuple[bool, ExecOutcome] | None:
|
|
59
|
+
if parsed is not None:
|
|
60
|
+
self._in_error_handler = False
|
|
61
|
+
arg = parsed.arg
|
|
62
|
+
else:
|
|
63
|
+
m = RE_RESUME.match(stmt)
|
|
64
|
+
if not m:
|
|
65
|
+
return None
|
|
66
|
+
self._in_error_handler = False
|
|
67
|
+
arg = m.group(1)
|
|
68
|
+
if arg is None or arg.strip() == '':
|
|
69
|
+
# RESUME — retry the line that caused the error
|
|
70
|
+
for i, ln in enumerate(sorted_lines):
|
|
71
|
+
if ln == self._err_line:
|
|
72
|
+
return True, i
|
|
73
|
+
return True, ExecResult.ADVANCE
|
|
74
|
+
if arg.strip().upper() == 'NEXT':
|
|
75
|
+
for i, ln in enumerate(sorted_lines):
|
|
76
|
+
if ln == self._err_line:
|
|
77
|
+
return True, i + 1
|
|
78
|
+
return True, ExecResult.ADVANCE
|
|
79
|
+
# RESUME <line>
|
|
80
|
+
target = int(arg.strip())
|
|
81
|
+
for i, ln in enumerate(sorted_lines):
|
|
82
|
+
if ln == target:
|
|
83
|
+
return True, i
|
|
84
|
+
raise RuntimeError(f"RESUME: LINE {target} NOT FOUND")
|
|
85
|
+
|
|
86
|
+
def _cf_error(self, stmt: str, *, parsed=None) -> tuple[bool, ExecOutcome] | None:
|
|
87
|
+
if parsed is not None:
|
|
88
|
+
code = parsed.code
|
|
89
|
+
else:
|
|
90
|
+
m = RE_ERROR_STMT.match(stmt)
|
|
91
|
+
if not m:
|
|
92
|
+
return None
|
|
93
|
+
code = int(m.group(1))
|
|
94
|
+
raise RuntimeError(f"ERROR {code}")
|
|
95
|
+
|
|
96
|
+
def _handle_error(self, err: Exception, line_num: int,
|
|
97
|
+
sorted_lines: list[int]) -> int | None:
|
|
98
|
+
"""Handle a runtime error. Returns ip to jump to, or None to propagate."""
|
|
99
|
+
if self._error_target is not None and not self._in_error_handler:
|
|
100
|
+
self._err_code = 1
|
|
101
|
+
self._err_line = line_num
|
|
102
|
+
self._in_error_handler = True
|
|
103
|
+
# Extract error code from "ERROR N" messages
|
|
104
|
+
msg = str(err)
|
|
105
|
+
if msg.startswith('ERROR '):
|
|
106
|
+
try:
|
|
107
|
+
self._err_code = int(msg.split()[1])
|
|
108
|
+
except (IndexError, ValueError):
|
|
109
|
+
pass
|
|
110
|
+
self.variables['ERR'] = self._err_code
|
|
111
|
+
self.variables['ERL'] = self._err_line
|
|
112
|
+
for i, ln in enumerate(sorted_lines):
|
|
113
|
+
if ln == self._error_target:
|
|
114
|
+
return i
|
|
115
|
+
return None
|
|
116
|
+
|
|
117
|
+
# ── ASSERT ─────────────────────────────────────────────────────────
|
|
118
|
+
|
|
119
|
+
def _cf_assert(self, stmt: str, run_vars: dict[str, Any], *, parsed=None) -> tuple[bool, ExecOutcome] | None:
|
|
120
|
+
if parsed is not None:
|
|
121
|
+
cond = parsed.condition
|
|
122
|
+
else:
|
|
123
|
+
m = RE_ASSERT.match(stmt)
|
|
124
|
+
if not m:
|
|
125
|
+
return None
|
|
126
|
+
cond = m.group(1).strip()
|
|
127
|
+
if not self._eval_condition(cond, run_vars):
|
|
128
|
+
raise RuntimeError(f"ASSERTION FAILED: {cond}")
|
|
129
|
+
return True, ExecResult.ADVANCE
|
|
130
|
+
|
|
131
|
+
# ── Time-travel debugging ─────────────────────────────────────────
|
|
132
|
+
|
|
133
|
+
def _checkpoint_sv(self, line_num: int) -> None:
|
|
134
|
+
"""Save a statevector checkpoint (for small qubit counts)."""
|
|
135
|
+
if self.last_sv is not None and self.num_qubits <= 16:
|
|
136
|
+
import numpy as np
|
|
137
|
+
self._sv_checkpoints.append((line_num, np.array(self.last_sv).copy()))
|
|
138
|
+
if len(self._sv_checkpoints) > MAX_SV_CHECKPOINTS:
|
|
139
|
+
excess = len(self._sv_checkpoints) - MAX_SV_CHECKPOINTS
|
|
140
|
+
del self._sv_checkpoints[:excess]
|
|
141
|
+
self._tt_position = len(self._sv_checkpoints) - 1
|
|
142
|
+
|
|
143
|
+
def cmd_rewind(self, rest: str = '') -> None:
|
|
144
|
+
"""REWIND [N] — go back N steps in the statevector history."""
|
|
145
|
+
if not self._sv_checkpoints:
|
|
146
|
+
self.io.writeln("?NO CHECKPOINTS — use STEP mode to build history")
|
|
147
|
+
return
|
|
148
|
+
n = int(rest.strip()) if rest.strip() else 1
|
|
149
|
+
new_pos = max(0, self._tt_position - n)
|
|
150
|
+
self._tt_position = new_pos
|
|
151
|
+
line_num, sv = self._sv_checkpoints[new_pos]
|
|
152
|
+
self.last_sv = sv.copy()
|
|
153
|
+
self.io.writeln(f"REWIND to step {new_pos} (line {line_num})")
|
|
154
|
+
self._print_sv_compact(sv)
|
|
155
|
+
|
|
156
|
+
def cmd_forward(self, rest: str = '') -> None:
|
|
157
|
+
"""FORWARD [N] — go forward N steps in the statevector history."""
|
|
158
|
+
if not self._sv_checkpoints:
|
|
159
|
+
self.io.writeln("?NO CHECKPOINTS")
|
|
160
|
+
return
|
|
161
|
+
n = int(rest.strip()) if rest.strip() else 1
|
|
162
|
+
new_pos = min(len(self._sv_checkpoints) - 1, self._tt_position + n)
|
|
163
|
+
self._tt_position = new_pos
|
|
164
|
+
line_num, sv = self._sv_checkpoints[new_pos]
|
|
165
|
+
self.last_sv = sv.copy()
|
|
166
|
+
self.io.writeln(f"FORWARD to step {new_pos} (line {line_num})")
|
|
167
|
+
self._print_sv_compact(sv)
|
|
168
|
+
|
|
169
|
+
def cmd_history(self, rest: str = '') -> None:
|
|
170
|
+
"""HISTORY — show statevector checkpoint list."""
|
|
171
|
+
if not self._sv_checkpoints:
|
|
172
|
+
self.io.writeln("?NO CHECKPOINTS")
|
|
173
|
+
return
|
|
174
|
+
for i, (ln, _) in enumerate(self._sv_checkpoints):
|
|
175
|
+
marker = " <<" if i == self._tt_position else ""
|
|
176
|
+
self.io.writeln(f" [{i}] line {ln}{marker}")
|
|
177
|
+
|
|
178
|
+
# ── TRON / TROFF ───────────────────────────────────────────────────
|
|
179
|
+
|
|
180
|
+
def cmd_tron(self) -> None:
|
|
181
|
+
"""TRON — trace on, print each line number during execution."""
|
|
182
|
+
self._trace_mode = True
|
|
183
|
+
self.io.writeln("TRACE ON")
|
|
184
|
+
|
|
185
|
+
def cmd_troff(self) -> None:
|
|
186
|
+
"""TROFF — trace off."""
|
|
187
|
+
self._trace_mode = False
|
|
188
|
+
self.io.writeln("TRACE OFF")
|
|
189
|
+
|
|
190
|
+
def _trace_line(self, line_num: int) -> None:
|
|
191
|
+
"""Print trace output if trace mode is on."""
|
|
192
|
+
if self._trace_mode:
|
|
193
|
+
self.io.write(f"[{line_num}]" + ' ')
|
|
194
|
+
|
|
195
|
+
# ── STOP / CONT ────────────────────────────────────────────────────
|
|
196
|
+
|
|
197
|
+
def _cf_stop(self, stmt: str, sorted_lines: list[int], ip: int, *, parsed=None) -> tuple[bool, ExecOutcome] | None:
|
|
198
|
+
if parsed is None and stmt.strip().upper() != 'STOP':
|
|
199
|
+
return None
|
|
200
|
+
# CONT sets _cont_skip_stop_ip to replay past this STOP
|
|
201
|
+
if self._cont_skip_stop_ip is not None and self._cont_skip_stop_ip == ip:
|
|
202
|
+
self._cont_skip_stop_ip = None
|
|
203
|
+
return True, ExecResult.ADVANCE
|
|
204
|
+
line_num = sorted_lines[ip]
|
|
205
|
+
self.io.writeln(f"STOPPED AT LINE {line_num}")
|
|
206
|
+
self._stopped_ip = ip
|
|
207
|
+
self._print_watches()
|
|
208
|
+
return True, ExecResult.END
|
|
209
|
+
|
|
210
|
+
def cmd_cont(self) -> None:
|
|
211
|
+
"""CONT — continue execution after STOP, resuming from the next line.
|
|
212
|
+
|
|
213
|
+
Re-executes the program from the top but skips the STOP that fired,
|
|
214
|
+
so lines after STOP are reached. Variable state is rebuilt by
|
|
215
|
+
replaying all lines before the STOP.
|
|
216
|
+
"""
|
|
217
|
+
if self._stopped_ip is None:
|
|
218
|
+
self.io.writeln("?CANNOT CONTINUE")
|
|
219
|
+
return
|
|
220
|
+
self.io.writeln("CONTINUING...")
|
|
221
|
+
self._cont_skip_stop_ip = self._stopped_ip
|
|
222
|
+
self._stopped_ip = None
|
|
223
|
+
self.cmd_run()
|
|
224
|
+
|
|
225
|
+
# ── Breakpoints ────────────────────────────────────────────────────
|
|
226
|
+
|
|
227
|
+
def cmd_breakpoint(self, rest: str) -> None:
|
|
228
|
+
"""BREAK <line> — set/clear/list breakpoints."""
|
|
229
|
+
if not rest.strip():
|
|
230
|
+
if self._breakpoints:
|
|
231
|
+
self.io.writeln(f" Breakpoints: {sorted(self._breakpoints)}")
|
|
232
|
+
else:
|
|
233
|
+
self.io.writeln(" No breakpoints set")
|
|
234
|
+
return
|
|
235
|
+
rest = rest.strip().upper()
|
|
236
|
+
if rest == 'CLEAR':
|
|
237
|
+
self._breakpoints.clear()
|
|
238
|
+
self.io.writeln("BREAKPOINTS CLEARED")
|
|
239
|
+
return
|
|
240
|
+
try:
|
|
241
|
+
line = int(rest)
|
|
242
|
+
if line in self._breakpoints:
|
|
243
|
+
self._breakpoints.discard(line)
|
|
244
|
+
self.io.writeln(f"BREAKPOINT REMOVED: {line}")
|
|
245
|
+
else:
|
|
246
|
+
self._breakpoints.add(line)
|
|
247
|
+
self.io.writeln(f"BREAKPOINT SET: {line}")
|
|
248
|
+
except ValueError:
|
|
249
|
+
self.io.writeln("?USAGE: BREAK <line> | BREAK CLEAR")
|
|
250
|
+
|
|
251
|
+
def _check_breakpoint(self, line_num: int, sorted_lines: list[int], ip: int) -> bool:
|
|
252
|
+
"""Check if we should break at this line. Returns True to stop."""
|
|
253
|
+
if line_num in self._breakpoints:
|
|
254
|
+
self.io.writeln(f"BREAKPOINT AT LINE {line_num}")
|
|
255
|
+
self._stopped_ip = ip
|
|
256
|
+
self._print_watches()
|
|
257
|
+
return True
|
|
258
|
+
return False
|
|
259
|
+
|
|
260
|
+
# ── Watch expressions ──────────────────────────────────────────────
|
|
261
|
+
|
|
262
|
+
def cmd_watch(self, rest: str) -> None:
|
|
263
|
+
"""WATCH <expr> — add/remove/list watch expressions."""
|
|
264
|
+
if not rest.strip():
|
|
265
|
+
if self._watches:
|
|
266
|
+
self.io.writeln(" Watch expressions:")
|
|
267
|
+
for w in self._watches:
|
|
268
|
+
self.io.writeln(f" {w}")
|
|
269
|
+
else:
|
|
270
|
+
self.io.writeln(" No watches set")
|
|
271
|
+
return
|
|
272
|
+
rest = rest.strip()
|
|
273
|
+
if rest.upper() == 'CLEAR':
|
|
274
|
+
self._watches.clear()
|
|
275
|
+
self.io.writeln("WATCHES CLEARED")
|
|
276
|
+
return
|
|
277
|
+
if rest in self._watches:
|
|
278
|
+
self._watches.remove(rest)
|
|
279
|
+
self.io.writeln(f"WATCH REMOVED: {rest}")
|
|
280
|
+
else:
|
|
281
|
+
self._watches.append(rest)
|
|
282
|
+
self.io.writeln(f"WATCHING: {rest}")
|
|
283
|
+
|
|
284
|
+
def _print_watches(self) -> None:
|
|
285
|
+
for w in self._watches:
|
|
286
|
+
try:
|
|
287
|
+
val = self._safe_eval(w)
|
|
288
|
+
self.io.writeln(f" {w} = {val}")
|
|
289
|
+
except Exception:
|
|
290
|
+
self.io.writeln(f" {w} = ?")
|
|
291
|
+
|
|
292
|
+
# ── Callbacks ──────────────────────────────────────────────────────
|
|
293
|
+
|
|
294
|
+
def _cf_on_measure(self, stmt: str, *, parsed=None) -> tuple[bool, ExecOutcome] | None:
|
|
295
|
+
if parsed is not None:
|
|
296
|
+
self._on_measure_target = parsed.target
|
|
297
|
+
else:
|
|
298
|
+
m = RE_ON_MEASURE.match(stmt)
|
|
299
|
+
if not m:
|
|
300
|
+
return None
|
|
301
|
+
self._on_measure_target = int(m.group(1))
|
|
302
|
+
return True, ExecResult.ADVANCE
|
|
303
|
+
|
|
304
|
+
def _cf_on_timer(self, stmt: str, *, parsed=None) -> tuple[bool, ExecOutcome] | None:
|
|
305
|
+
if parsed is not None:
|
|
306
|
+
self._on_timer_interval = float(parsed.interval)
|
|
307
|
+
self._on_timer_target = parsed.target
|
|
308
|
+
else:
|
|
309
|
+
m = RE_ON_TIMER.match(stmt)
|
|
310
|
+
if not m:
|
|
311
|
+
return None
|
|
312
|
+
self._on_timer_interval = float(m.group(1))
|
|
313
|
+
self._on_timer_target = int(m.group(2))
|
|
314
|
+
self._on_timer_last = time.time()
|
|
315
|
+
return True, ExecResult.ADVANCE
|
|
316
|
+
|
|
317
|
+
def _check_timer_callback(self, sorted_lines: list[int], ip: int) -> int | None:
|
|
318
|
+
"""Check if ON TIMER callback should fire. Returns ip to jump to, or None."""
|
|
319
|
+
if self._on_timer_target is not None and self._on_timer_interval > 0:
|
|
320
|
+
now = time.time()
|
|
321
|
+
if now - self._on_timer_last >= self._on_timer_interval:
|
|
322
|
+
self._on_timer_last = now
|
|
323
|
+
self._gosub_stack.append(ip)
|
|
324
|
+
for i, ln in enumerate(sorted_lines):
|
|
325
|
+
if ln == self._on_timer_target:
|
|
326
|
+
return i
|
|
327
|
+
return None
|
qbasic_core/demos.py
ADDED
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
"""QBASIC built-in demo circuits."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class DemoMixin:
|
|
5
|
+
"""Built-in quantum computing demos.
|
|
6
|
+
|
|
7
|
+
Requires: TerminalProtocol — uses self.program, self.num_qubits,
|
|
8
|
+
self.shots, self.cmd_new(), self.cmd_run(), self.cmd_list(), self.cmd_locc().
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
def cmd_demo(self, name):
|
|
12
|
+
demos = {
|
|
13
|
+
'BELL': self._demo_bell,
|
|
14
|
+
'GHZ': self._demo_ghz,
|
|
15
|
+
'TELEPORT': self._demo_teleport,
|
|
16
|
+
'GROVER': self._demo_grover,
|
|
17
|
+
'QFT': self._demo_qft,
|
|
18
|
+
'DEUTSCH': self._demo_deutsch,
|
|
19
|
+
'BERNSTEIN': self._demo_bernstein,
|
|
20
|
+
'SUPERDENSE': self._demo_superdense,
|
|
21
|
+
'RANDOM': self._demo_random,
|
|
22
|
+
'STRESS': self._demo_stress,
|
|
23
|
+
'LOCC-TELEPORT': self._demo_locc_teleport,
|
|
24
|
+
'LOCC-COORD': self._demo_locc_coord,
|
|
25
|
+
}
|
|
26
|
+
name = name.upper().strip()
|
|
27
|
+
if not name or name == 'LIST':
|
|
28
|
+
self.io.writeln(" Available demos:")
|
|
29
|
+
for d in demos:
|
|
30
|
+
prefix = " (LOCC)" if d.startswith('LOCC') else ""
|
|
31
|
+
self.io.writeln(f" DEMO {d}{prefix}")
|
|
32
|
+
return
|
|
33
|
+
if name not in demos:
|
|
34
|
+
self.io.writeln(f"?UNKNOWN DEMO: {name}")
|
|
35
|
+
self.io.writeln(f" Try: DEMO LIST")
|
|
36
|
+
return
|
|
37
|
+
demos[name]()
|
|
38
|
+
|
|
39
|
+
def _demo_bell(self):
|
|
40
|
+
self.cmd_new()
|
|
41
|
+
self.num_qubits = 2
|
|
42
|
+
self.program = {
|
|
43
|
+
10: "REM === Bell State ===",
|
|
44
|
+
20: "REM The simplest entanglement. Two qubits, perfectly correlated.",
|
|
45
|
+
30: "H 0",
|
|
46
|
+
40: "CX 0,1",
|
|
47
|
+
50: "MEASURE",
|
|
48
|
+
}
|
|
49
|
+
self.io.writeln("LOADED: Bell State (2 qubits)")
|
|
50
|
+
self.cmd_list()
|
|
51
|
+
self.cmd_run()
|
|
52
|
+
|
|
53
|
+
def _demo_ghz(self):
|
|
54
|
+
self.cmd_new()
|
|
55
|
+
n = min(self.num_qubits, 8)
|
|
56
|
+
self.num_qubits = n
|
|
57
|
+
self.program = {
|
|
58
|
+
10: "REM === GHZ State ===",
|
|
59
|
+
20: f"REM {n}-qubit Greenberger-Horne-Zeilinger state",
|
|
60
|
+
30: "REM All qubits entangled: 50% all-zeros, 50% all-ones.",
|
|
61
|
+
40: "REM Einstein called this 'spooky action at a distance.'",
|
|
62
|
+
50: "H 0",
|
|
63
|
+
}
|
|
64
|
+
line = 60
|
|
65
|
+
for i in range(1, n):
|
|
66
|
+
self.program[line] = f"CX 0,{i}"
|
|
67
|
+
line += 10
|
|
68
|
+
self.program[line] = "MEASURE"
|
|
69
|
+
self.io.writeln(f"LOADED: GHZ State ({n} qubits)")
|
|
70
|
+
self.cmd_list()
|
|
71
|
+
self.cmd_run()
|
|
72
|
+
|
|
73
|
+
def _demo_teleport(self):
|
|
74
|
+
self.cmd_new()
|
|
75
|
+
self.num_qubits = 3
|
|
76
|
+
self.program = {
|
|
77
|
+
10: "REM === Quantum Teleportation (circuit only) ===",
|
|
78
|
+
20: "REM Qubit 0: state to teleport (prepared as |+>)",
|
|
79
|
+
30: "REM Qubit 1-2: entangled Bell pair (the 'channel')",
|
|
80
|
+
40: "REM Shows the entangling stage of teleportation.",
|
|
81
|
+
50: "REM Classical correction requires mid-circuit measurement",
|
|
82
|
+
60: "REM with feedforward, which needs LOCC mode.",
|
|
83
|
+
70: "REM >>> For full teleportation: DEMO LOCC-TELEPORT <<<",
|
|
84
|
+
80: "REM",
|
|
85
|
+
90: "REM Prepare state to teleport (|+> on qubit 0)",
|
|
86
|
+
100: "H 0",
|
|
87
|
+
110: "REM Create Bell pair on qubits 1,2",
|
|
88
|
+
120: "H 1",
|
|
89
|
+
130: "CX 1,2",
|
|
90
|
+
140: "REM Teleportation circuit (without classical correction)",
|
|
91
|
+
150: "CX 0,1",
|
|
92
|
+
160: "H 0",
|
|
93
|
+
170: "MEASURE",
|
|
94
|
+
}
|
|
95
|
+
self.io.writeln("LOADED: Quantum Teleportation — circuit stage (3 qubits)")
|
|
96
|
+
self.io.writeln(" Without correction: uniform over all 8 outcomes.")
|
|
97
|
+
self.io.writeln(" For full protocol with correction: DEMO LOCC-TELEPORT")
|
|
98
|
+
self.cmd_list()
|
|
99
|
+
self.cmd_run()
|
|
100
|
+
|
|
101
|
+
def _demo_grover(self):
|
|
102
|
+
self.cmd_new()
|
|
103
|
+
self.num_qubits = 3
|
|
104
|
+
self.shots = 1024
|
|
105
|
+
self.program = {
|
|
106
|
+
10: "REM === Grover's Search Algorithm ===",
|
|
107
|
+
20: "REM Searching for |101> among 8 states",
|
|
108
|
+
30: "REM Classically: ~4 queries. Quantumly: 2 iterations, ~95%.",
|
|
109
|
+
40: "REM Quadratic speedup over classical unstructured search.",
|
|
110
|
+
50: "REM",
|
|
111
|
+
60: "REM Superposition",
|
|
112
|
+
70: "H 0 : H 1 : H 2",
|
|
113
|
+
80: "REM",
|
|
114
|
+
90: "REM === Grover iteration 1 ===",
|
|
115
|
+
100: "REM Oracle: flip phase of |101> (X on q1, CCZ, undo X)",
|
|
116
|
+
110: "X 1",
|
|
117
|
+
120: "H 2 : CCX 0,1,2 : H 2",
|
|
118
|
+
130: "X 1",
|
|
119
|
+
140: "REM Diffusion: 2|s><s| - I",
|
|
120
|
+
150: "H 0 : H 1 : H 2",
|
|
121
|
+
160: "X 0 : X 1 : X 2",
|
|
122
|
+
170: "H 2 : CCX 0,1,2 : H 2",
|
|
123
|
+
180: "X 0 : X 1 : X 2",
|
|
124
|
+
190: "H 0 : H 1 : H 2",
|
|
125
|
+
200: "REM",
|
|
126
|
+
210: "REM === Grover iteration 2 ===",
|
|
127
|
+
220: "X 1",
|
|
128
|
+
230: "H 2 : CCX 0,1,2 : H 2",
|
|
129
|
+
240: "X 1",
|
|
130
|
+
250: "H 0 : H 1 : H 2",
|
|
131
|
+
260: "X 0 : X 1 : X 2",
|
|
132
|
+
270: "H 2 : CCX 0,1,2 : H 2",
|
|
133
|
+
280: "X 0 : X 1 : X 2",
|
|
134
|
+
290: "H 0 : H 1 : H 2",
|
|
135
|
+
300: "REM",
|
|
136
|
+
310: "MEASURE",
|
|
137
|
+
}
|
|
138
|
+
self.io.writeln("LOADED: Grover's Search (3 qubits, target=|101>)")
|
|
139
|
+
self.cmd_list()
|
|
140
|
+
self.cmd_run()
|
|
141
|
+
|
|
142
|
+
def _demo_qft(self):
|
|
143
|
+
self.cmd_new()
|
|
144
|
+
self.num_qubits = 4
|
|
145
|
+
self.program = {
|
|
146
|
+
10: "REM === Quantum Fourier Transform ===",
|
|
147
|
+
20: "REM The quantum analog of the discrete Fourier transform.",
|
|
148
|
+
30: "REM Exponentially faster than classical FFT.",
|
|
149
|
+
40: "REM QFT is typically used as a subroutine (phase info lost on measure).",
|
|
150
|
+
50: "REM",
|
|
151
|
+
60: "REM Prepare input state |5> = |0101>",
|
|
152
|
+
70: "X 0 : X 2",
|
|
153
|
+
80: "REM",
|
|
154
|
+
90: "REM QFT on 4 qubits",
|
|
155
|
+
100: "H 3",
|
|
156
|
+
110: "CP PI/2, 2, 3",
|
|
157
|
+
120: "CP PI/4, 1, 3",
|
|
158
|
+
130: "CP PI/8, 0, 3",
|
|
159
|
+
140: "H 2",
|
|
160
|
+
150: "CP PI/2, 1, 2",
|
|
161
|
+
160: "CP PI/4, 0, 2",
|
|
162
|
+
170: "H 1",
|
|
163
|
+
180: "CP PI/2, 0, 1",
|
|
164
|
+
190: "H 0",
|
|
165
|
+
200: "REM Swap for bit reversal",
|
|
166
|
+
210: "SWAP 0,3",
|
|
167
|
+
220: "SWAP 1,2",
|
|
168
|
+
230: "MEASURE",
|
|
169
|
+
}
|
|
170
|
+
self.io.writeln("LOADED: QFT on |0101> (4 qubits)")
|
|
171
|
+
self.cmd_list()
|
|
172
|
+
self.cmd_run()
|
|
173
|
+
|
|
174
|
+
def _demo_deutsch(self):
|
|
175
|
+
self.cmd_new()
|
|
176
|
+
self.num_qubits = 2
|
|
177
|
+
self.program = {
|
|
178
|
+
10: "REM === Deutsch-Jozsa Algorithm ===",
|
|
179
|
+
20: "REM Determines if a function is constant or balanced",
|
|
180
|
+
30: "REM in ONE query. Classically needs 2.",
|
|
181
|
+
40: "REM Published 1992. The first to demonstrate quantum advantage.",
|
|
182
|
+
50: "REM",
|
|
183
|
+
60: "REM Oracle qubit in |->",
|
|
184
|
+
70: "X 1",
|
|
185
|
+
80: "H 0 : H 1",
|
|
186
|
+
90: "REM Oracle (balanced function: f(x) = x)",
|
|
187
|
+
100: "CX 0,1",
|
|
188
|
+
110: "REM Interfere and measure",
|
|
189
|
+
120: "H 0",
|
|
190
|
+
130: "MEASURE",
|
|
191
|
+
}
|
|
192
|
+
self.io.writeln("LOADED: Deutsch-Jozsa (2 qubits, balanced oracle)")
|
|
193
|
+
self.io.writeln(" Expect: qubit 0 = 1 (balanced)")
|
|
194
|
+
self.cmd_list()
|
|
195
|
+
self.cmd_run()
|
|
196
|
+
|
|
197
|
+
def _demo_bernstein(self):
|
|
198
|
+
self.cmd_new()
|
|
199
|
+
self.num_qubits = 5
|
|
200
|
+
self.program = {
|
|
201
|
+
10: "REM === Bernstein-Vazirani Algorithm ===",
|
|
202
|
+
20: "REM Finds a hidden bit string s in ONE query.",
|
|
203
|
+
30: "REM Secret: s = 1011 (4 bits, display order q3q2q1q0)",
|
|
204
|
+
40: "REM Classically you'd need 4 queries. Quantum: 1.",
|
|
205
|
+
50: "REM",
|
|
206
|
+
60: "REM Ancilla in |->",
|
|
207
|
+
70: "X 4",
|
|
208
|
+
80: "H 0 : H 1 : H 2 : H 3 : H 4",
|
|
209
|
+
90: "REM Oracle: CX from each bit where s=1",
|
|
210
|
+
100: "CX 0,4",
|
|
211
|
+
110: "CX 1,4",
|
|
212
|
+
120: "CX 3,4",
|
|
213
|
+
130: "REM Interfere",
|
|
214
|
+
140: "H 0 : H 1 : H 2 : H 3",
|
|
215
|
+
150: "MEASURE",
|
|
216
|
+
}
|
|
217
|
+
self.io.writeln("LOADED: Bernstein-Vazirani (5 qubits, secret=1011)")
|
|
218
|
+
self.io.writeln(" Expect: measurement shows ...1011 (q3q2q1q0)")
|
|
219
|
+
self.cmd_list()
|
|
220
|
+
self.cmd_run()
|
|
221
|
+
|
|
222
|
+
def _demo_superdense(self):
|
|
223
|
+
self.cmd_new()
|
|
224
|
+
self.num_qubits = 2
|
|
225
|
+
self.program = {
|
|
226
|
+
10: "REM === Superdense Coding ===",
|
|
227
|
+
20: "REM Send 2 classical bits using 1 qubit.",
|
|
228
|
+
30: "REM Encoding: 00->I, 01->X, 10->Z, 11->ZX (=iY)",
|
|
229
|
+
40: "REM Sending message '11'",
|
|
230
|
+
50: "REM",
|
|
231
|
+
60: "REM Create Bell pair",
|
|
232
|
+
70: "H 0",
|
|
233
|
+
80: "CX 0,1",
|
|
234
|
+
90: "REM Encode '11': apply Z then X to qubit 0",
|
|
235
|
+
100: "Z 0",
|
|
236
|
+
110: "X 0",
|
|
237
|
+
120: "REM Decode: reverse Bell circuit",
|
|
238
|
+
130: "CX 0,1",
|
|
239
|
+
140: "H 0",
|
|
240
|
+
150: "MEASURE",
|
|
241
|
+
}
|
|
242
|
+
self.io.writeln("LOADED: Superdense Coding (message='11')")
|
|
243
|
+
self.io.writeln(" Expect: |11> with high probability")
|
|
244
|
+
self.cmd_list()
|
|
245
|
+
self.cmd_run()
|
|
246
|
+
|
|
247
|
+
def _demo_random(self):
|
|
248
|
+
self.cmd_new()
|
|
249
|
+
n = min(self.num_qubits, 8)
|
|
250
|
+
self.num_qubits = n
|
|
251
|
+
self.shots = 1
|
|
252
|
+
self.program = {
|
|
253
|
+
10: "REM === Quantum Random Number Generator ===",
|
|
254
|
+
20: f"REM {n} qubits = {n}-bit truly random number",
|
|
255
|
+
30: "REM (as random as a simulator's PRNG allows)",
|
|
256
|
+
}
|
|
257
|
+
line = 40
|
|
258
|
+
for i in range(n):
|
|
259
|
+
self.program[line] = f"H {i}"
|
|
260
|
+
line += 10
|
|
261
|
+
self.program[line] = "MEASURE"
|
|
262
|
+
self.io.writeln(f"LOADED: Quantum RNG ({n} bits)")
|
|
263
|
+
self.cmd_list()
|
|
264
|
+
self.cmd_run()
|
|
265
|
+
self.shots = 1024
|
|
266
|
+
|
|
267
|
+
def _demo_stress(self):
|
|
268
|
+
self.cmd_new()
|
|
269
|
+
n = 20
|
|
270
|
+
self.num_qubits = n
|
|
271
|
+
self.program = {
|
|
272
|
+
10: f"REM === {n}-Qubit Stress Test ===",
|
|
273
|
+
20: f"REM H on all {n} qubits + nearest-neighbor CX chain",
|
|
274
|
+
30: f"REM Creates a highly entangled {n}-qubit state",
|
|
275
|
+
}
|
|
276
|
+
line = 40
|
|
277
|
+
for i in range(n):
|
|
278
|
+
self.program[line] = f"H {i}"
|
|
279
|
+
line += 10
|
|
280
|
+
for i in range(n - 1):
|
|
281
|
+
self.program[line] = f"CX {i},{i+1}"
|
|
282
|
+
line += 10
|
|
283
|
+
for i in range(n):
|
|
284
|
+
self.program[line] = f"RZ PI/4,{i}"
|
|
285
|
+
line += 10
|
|
286
|
+
for i in range(0, n - 1, 2):
|
|
287
|
+
self.program[line] = f"CX {i},{i+1}"
|
|
288
|
+
line += 10
|
|
289
|
+
self.program[line] = "MEASURE"
|
|
290
|
+
self.io.writeln(f"LOADED: {n}-Qubit Stress Test")
|
|
291
|
+
self.cmd_run()
|
|
292
|
+
|
|
293
|
+
def _demo_locc_teleport(self):
|
|
294
|
+
"""Quantum teleportation across A/B boundary (JOINT mode)."""
|
|
295
|
+
self.cmd_new()
|
|
296
|
+
self.cmd_locc('JOINT 3 3')
|
|
297
|
+
self.shots = 1024
|
|
298
|
+
self.program = {
|
|
299
|
+
10: "REM === LOCC Teleportation ===",
|
|
300
|
+
20: "REM Alice (A) teleports qubit 0 to Bob (B) qubit 0",
|
|
301
|
+
30: "REM Pre-shared Bell pair: A[2] <-> B[0]",
|
|
302
|
+
40: "REM",
|
|
303
|
+
50: "REM Prepare state to teleport: |+> on A[0]",
|
|
304
|
+
60: "@A H 0",
|
|
305
|
+
70: "REM Create shared entanglement",
|
|
306
|
+
80: "SHARE A 2, B 0",
|
|
307
|
+
90: "REM",
|
|
308
|
+
100: "REM === Teleportation protocol ===",
|
|
309
|
+
110: "REM Alice entangles her data qubit with her half of the pair",
|
|
310
|
+
120: "@A CX 0, 2",
|
|
311
|
+
130: "@A H 0",
|
|
312
|
+
140: "REM Alice measures and sends classical bits to Bob",
|
|
313
|
+
150: "SEND A 0 -> m0",
|
|
314
|
+
160: "SEND A 2 -> m1",
|
|
315
|
+
170: "REM Bob applies corrections based on Alice's results",
|
|
316
|
+
180: "IF m1 THEN @B X 0",
|
|
317
|
+
190: "IF m0 THEN @B Z 0",
|
|
318
|
+
200: "REM Bob's qubit 0 now holds Alice's original state",
|
|
319
|
+
210: "MEASURE",
|
|
320
|
+
}
|
|
321
|
+
self.io.writeln("LOADED: LOCC Teleportation (JOINT 3+3)")
|
|
322
|
+
self.io.writeln(" Alice sends |+> to Bob. Expect Bob's qubit ~ 50/50.")
|
|
323
|
+
self.cmd_list()
|
|
324
|
+
self.cmd_run()
|
|
325
|
+
|
|
326
|
+
def _demo_locc_coord(self):
|
|
327
|
+
"""Classical coordination between independent registers (SPLIT mode)."""
|
|
328
|
+
self.cmd_new()
|
|
329
|
+
self.cmd_locc('SPLIT 4 4')
|
|
330
|
+
self.shots = 1024
|
|
331
|
+
self.program = {
|
|
332
|
+
10: "REM === LOCC Classical Coordination ===",
|
|
333
|
+
20: "REM Two independent quantum processors sharing classical bits",
|
|
334
|
+
30: "REM Alice generates a random 2-bit key, Bob copies it",
|
|
335
|
+
40: "REM",
|
|
336
|
+
50: "REM Alice: generate random bits",
|
|
337
|
+
60: "@A H 0 : @A H 1",
|
|
338
|
+
70: "SEND A 0 -> k0",
|
|
339
|
+
80: "SEND A 1 -> k1",
|
|
340
|
+
90: "REM Bob: set qubits to match Alice's key",
|
|
341
|
+
100: "IF k0 THEN @B X 0",
|
|
342
|
+
110: "IF k1 THEN @B X 1",
|
|
343
|
+
120: "REM Both sides now agree on a 2-bit key",
|
|
344
|
+
130: "REM Alice uses her key to prepare an entangled state",
|
|
345
|
+
140: "@A H 2",
|
|
346
|
+
150: "@A CX 2,3",
|
|
347
|
+
160: "REM Bob does the same — they independently create matching states",
|
|
348
|
+
170: "@B H 2",
|
|
349
|
+
180: "@B CX 2,3",
|
|
350
|
+
190: "MEASURE",
|
|
351
|
+
}
|
|
352
|
+
self.io.writeln("LOADED: LOCC Classical Coordination (SPLIT 4+4)")
|
|
353
|
+
self.io.writeln(" Alice and Bob share a random key, then prepare matching states.")
|
|
354
|
+
self.cmd_list()
|
|
355
|
+
self.cmd_run()
|
|
356
|
+
|