qubasic 0.4.0__tar.gz → 0.4.1__tar.gz

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 (58) hide show
  1. {qubasic-0.4.0 → qubasic-0.4.1}/CHANGELOG.md +17 -0
  2. {qubasic-0.4.0 → qubasic-0.4.1}/LICENSE +1 -1
  3. {qubasic-0.4.0/qubasic.egg-info → qubasic-0.4.1}/PKG-INFO +1 -1
  4. {qubasic-0.4.0 → qubasic-0.4.1}/pyproject.toml +1 -1
  5. {qubasic-0.4.0 → qubasic-0.4.1/qubasic.egg-info}/PKG-INFO +1 -1
  6. {qubasic-0.4.0 → qubasic-0.4.1}/qubasic.py +17 -0
  7. {qubasic-0.4.0 → qubasic-0.4.1}/qubasic_core/__init__.py +1 -1
  8. {qubasic-0.4.0 → qubasic-0.4.1}/qubasic_core/classic.py +3 -0
  9. {qubasic-0.4.0 → qubasic-0.4.1}/qubasic_core/control_flow.py +5 -3
  10. {qubasic-0.4.0 → qubasic-0.4.1}/qubasic_core/demos.py +1 -1
  11. {qubasic-0.4.0 → qubasic-0.4.1}/qubasic_core/expression.py +9 -25
  12. {qubasic-0.4.0 → qubasic-0.4.1}/qubasic_core/locc_commands.py +9 -5
  13. {qubasic-0.4.0 → qubasic-0.4.1}/qubasic_core/locc_display.py +3 -3
  14. {qubasic-0.4.0 → qubasic-0.4.1}/qubasic_core/locc_execution.py +5 -5
  15. {qubasic-0.4.0 → qubasic-0.4.1}/qubasic_core/parser.py +2 -2
  16. {qubasic-0.4.0 → qubasic-0.4.1}/qubasic_core/profiler.py +6 -0
  17. {qubasic-0.4.0 → qubasic-0.4.1}/qubasic_core/scope.py +0 -1
  18. {qubasic-0.4.0 → qubasic-0.4.1}/qubasic_core/subs.py +25 -1
  19. {qubasic-0.4.0 → qubasic-0.4.1}/qubasic_core/terminal.py +2 -2
  20. {qubasic-0.4.0 → qubasic-0.4.1}/MANIFEST.in +0 -0
  21. {qubasic-0.4.0 → qubasic-0.4.1}/README.md +0 -0
  22. {qubasic-0.4.0 → qubasic-0.4.1}/examples/bell.qb +0 -0
  23. {qubasic-0.4.0 → qubasic-0.4.1}/examples/grover3.qb +0 -0
  24. {qubasic-0.4.0 → qubasic-0.4.1}/examples/locc_teleport.qb +0 -0
  25. {qubasic-0.4.0 → qubasic-0.4.1}/examples/sweep_rx.qb +0 -0
  26. {qubasic-0.4.0 → qubasic-0.4.1}/qubasic.egg-info/SOURCES.txt +0 -0
  27. {qubasic-0.4.0 → qubasic-0.4.1}/qubasic.egg-info/dependency_links.txt +0 -0
  28. {qubasic-0.4.0 → qubasic-0.4.1}/qubasic.egg-info/entry_points.txt +0 -0
  29. {qubasic-0.4.0 → qubasic-0.4.1}/qubasic.egg-info/requires.txt +0 -0
  30. {qubasic-0.4.0 → qubasic-0.4.1}/qubasic.egg-info/top_level.txt +0 -0
  31. {qubasic-0.4.0 → qubasic-0.4.1}/qubasic_core/__main__.py +0 -0
  32. {qubasic-0.4.0 → qubasic-0.4.1}/qubasic_core/analysis.py +0 -0
  33. {qubasic-0.4.0 → qubasic-0.4.1}/qubasic_core/backend.py +0 -0
  34. {qubasic-0.4.0 → qubasic-0.4.1}/qubasic_core/debug.py +0 -0
  35. {qubasic-0.4.0 → qubasic-0.4.1}/qubasic_core/display.py +0 -0
  36. {qubasic-0.4.0 → qubasic-0.4.1}/qubasic_core/engine.py +0 -0
  37. {qubasic-0.4.0 → qubasic-0.4.1}/qubasic_core/engine_state.py +0 -0
  38. {qubasic-0.4.0 → qubasic-0.4.1}/qubasic_core/errors.py +0 -0
  39. {qubasic-0.4.0 → qubasic-0.4.1}/qubasic_core/exec_context.py +0 -0
  40. {qubasic-0.4.0 → qubasic-0.4.1}/qubasic_core/executor.py +0 -0
  41. {qubasic-0.4.0 → qubasic-0.4.1}/qubasic_core/file_io.py +0 -0
  42. {qubasic-0.4.0 → qubasic-0.4.1}/qubasic_core/gates.py +0 -0
  43. {qubasic-0.4.0 → qubasic-0.4.1}/qubasic_core/help_text.py +0 -0
  44. {qubasic-0.4.0 → qubasic-0.4.1}/qubasic_core/io_protocol.py +0 -0
  45. {qubasic-0.4.0 → qubasic-0.4.1}/qubasic_core/locc.py +0 -0
  46. {qubasic-0.4.0 → qubasic-0.4.1}/qubasic_core/locc_engine.py +0 -0
  47. {qubasic-0.4.0 → qubasic-0.4.1}/qubasic_core/memory.py +0 -0
  48. {qubasic-0.4.0 → qubasic-0.4.1}/qubasic_core/mock_backend.py +0 -0
  49. {qubasic-0.4.0 → qubasic-0.4.1}/qubasic_core/noise_mixin.py +0 -0
  50. {qubasic-0.4.0 → qubasic-0.4.1}/qubasic_core/patterns.py +0 -0
  51. {qubasic-0.4.0 → qubasic-0.4.1}/qubasic_core/program_mgmt.py +0 -0
  52. {qubasic-0.4.0 → qubasic-0.4.1}/qubasic_core/protocol.py +0 -0
  53. {qubasic-0.4.0 → qubasic-0.4.1}/qubasic_core/screen.py +0 -0
  54. {qubasic-0.4.0 → qubasic-0.4.1}/qubasic_core/state_display.py +0 -0
  55. {qubasic-0.4.0 → qubasic-0.4.1}/qubasic_core/statements.py +0 -0
  56. {qubasic-0.4.0 → qubasic-0.4.1}/qubasic_core/strings.py +0 -0
  57. {qubasic-0.4.0 → qubasic-0.4.1}/qubasic_core/sweep.py +0 -0
  58. {qubasic-0.4.0 → qubasic-0.4.1}/setup.cfg +0 -0
@@ -1,5 +1,22 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.1 (2026-03-30)
4
+
5
+ - **Fix SEED dispatch**: moved from no-arg to with-arg dispatch table so `SEED 42` works from the REPL
6
+ - **Fix GPU probe**: `cmd_method` GPU probe used undefined `_pqc` variable; now uses `_pqc_m`
7
+ - **Fix LOCC non-numeric args**: `LOCC 4 banana` no longer crashes with unguarded ValueError
8
+ - **Coverage threshold**: raised CI coverage floor from 60% to 75%
9
+ - **Property-based tests**: 4 new hypothesis tests (arithmetic identity, parser fuzzing, process fuzzing, FOR loop count)
10
+ - **CLI integration tests**: 8 new tests covering dispatch, SEED, LOCC error handling, METHOD probe, --seed flag, --help
11
+ - **Parser imports**: import regexes from patterns.py directly instead of double-indirection through engine.py
12
+ - **Expression simplification**: `_replace_dollar_outside_strings` reduced from two passes to one
13
+ - **Jump table**: pre-compute WHILE/WEND and DO/LOOP ip mappings in `_scan_subs`; `_find_matching_wend` and `_find_matching_loop` use O(1) lookup when available
14
+ - **Scope cleanup**: removed dual-write hack in `Scope.__setitem__`; writes go to `_runtime` only, `_persistent` is read-through fallback
15
+ - **STATS output**: redirect stdout during stats runs to suppress rich console output in non-TTY mode
16
+ - **Copyright year**: LICENSE updated from 2025 to 2026
17
+ - **CLI --seed flag**: `qubasic --seed N script.qb` sets deterministic seed before execution
18
+ - **Type annotations**: added return type annotations to 14 unannotated mixin methods across locc_commands, locc_display, locc_execution, demos
19
+
3
20
  ## 0.4.0 (2026-03-29)
4
21
 
5
22
  - **Noise correctness**: transpile with optimization_level=0 when noisy so gates survive for noise attachment
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025 Charles C. Norton
3
+ Copyright (c) 2026 Charles C. Norton
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: qubasic
3
- Version: 0.4.0
3
+ Version: 0.4.1
4
4
  Summary: Quantum BASIC Interactive Terminal
5
5
  Author-email: "Charles C. Norton" <machineelv@gmail.com>
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "qubasic"
7
- version = "0.4.0"
7
+ version = "0.4.1"
8
8
  description = "Quantum BASIC Interactive Terminal"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: qubasic
3
- Version: 0.4.0
3
+ Version: 0.4.1
4
4
  Summary: Quantum BASIC Interactive Terminal
5
5
  Author-email: "Charles C. Norton" <machineelv@gmail.com>
6
6
  License-Expression: MIT
@@ -53,10 +53,22 @@ def main():
53
53
  args = sys.argv[1:]
54
54
  quiet = '--quiet' in args or '-q' in args
55
55
  json_mode = '--json' in args
56
+ seed_val = None
56
57
  if quiet:
57
58
  args = [a for a in args if a not in ('--quiet', '-q')]
58
59
  if json_mode:
59
60
  args = [a for a in args if a != '--json']
61
+ # Parse --seed N
62
+ filtered = []
63
+ i = 0
64
+ while i < len(args):
65
+ if args[i] == '--seed' and i + 1 < len(args):
66
+ seed_val = int(args[i + 1])
67
+ i += 2
68
+ else:
69
+ filtered.append(args[i])
70
+ i += 1
71
+ args = filtered
60
72
 
61
73
  if any(a in ('-h', '--help') for a in args):
62
74
  from qubasic_core import __version__
@@ -67,12 +79,17 @@ def main():
67
79
  print(" qubasic script.qb Run a script file")
68
80
  print(" qubasic --quiet script Suppress banner and progress")
69
81
  print(" qubasic --json script Output results as JSON")
82
+ print(" qubasic --seed N script Set random seed for reproducibility")
70
83
  print(" qubasic --help Show this help")
71
84
  print()
72
85
  print("Type HELP inside the REPL for full command reference.")
73
86
  sys.exit(0)
74
87
 
75
88
  term = QBasicTerminal()
89
+ if seed_val is not None:
90
+ import numpy as _np
91
+ term._seed = seed_val
92
+ _np.random.seed(seed_val)
76
93
 
77
94
  if args:
78
95
  path = args[0]
@@ -28,7 +28,7 @@ __all__ = [
28
28
  'GATE_TABLE', 'GATE_ALIASES',
29
29
  ]
30
30
 
31
- __version__ = '0.4.0'
31
+ __version__ = '0.4.1'
32
32
 
33
33
  def __getattr__(name):
34
34
  """Lazy import heavy modules on first access."""
@@ -217,6 +217,9 @@ class ClassicMixin:
217
217
  # ── DO / LOOP ─────────────────────────────────────────────────────
218
218
 
219
219
  def _find_matching_loop(self, sorted_lines: list[int], ip: int) -> int:
220
+ jt = getattr(self, '_jump_table', None)
221
+ if jt and ip in jt:
222
+ return jt[ip]
220
223
  depth = 1
221
224
  scan = ip + 1
222
225
  while scan < len(sorted_lines):
@@ -166,10 +166,12 @@ class ControlFlowMixin:
166
166
  def _find_matching_wend(self, sorted_lines: list[int], ip: int) -> int:
167
167
  """Find the ip after the WEND matching the WHILE at ip.
168
168
 
169
- Scans forward with proper nesting depth tracking. Returns the ip
170
- index past the matching WEND. Raises with the WHILE line number
171
- for clear diagnostics.
169
+ Uses pre-computed jump table when available (O(1)), falls back
170
+ to linear scan with nesting depth tracking.
172
171
  """
172
+ jt = getattr(self, '_jump_table', None)
173
+ if jt and ip in jt:
174
+ return jt[ip] + 1
173
175
  depth = 1
174
176
  scan = ip + 1
175
177
  while scan < len(sorted_lines):
@@ -8,7 +8,7 @@ class DemoMixin:
8
8
  self.shots, self.cmd_new(), self.cmd_run(), self.cmd_list(), self.cmd_locc().
9
9
  """
10
10
 
11
- def cmd_demo(self, name):
11
+ def cmd_demo(self, name: str) -> None:
12
12
  demos = {
13
13
  'BELL': self._demo_bell,
14
14
  'GHZ': self._demo_ghz,
@@ -12,44 +12,28 @@ from typing import Any
12
12
 
13
13
 
14
14
  def _replace_dollar_outside_strings(expr_str: str) -> str:
15
- """Apply $->_S_ replacement only outside quoted string literals."""
16
- result: list[str] = []
15
+ """Apply $->_S_ replacement only outside quoted string literals.
16
+
17
+ Single pass: splits on quote boundaries, substitutes only in
18
+ unquoted segments.
19
+ """
20
+ parts: list[str] = []
17
21
  i = 0
18
22
  n = len(expr_str)
19
23
  while i < n:
20
24
  ch = expr_str[i]
21
25
  if ch in ('"', "'"):
22
- # Copy the entire quoted region verbatim
23
26
  quote = ch
24
27
  j = i + 1
25
28
  while j < n and expr_str[j] != quote:
26
29
  j += 1
27
- # Include closing quote (or end of string if missing)
28
- result.append(expr_str[i:j + 1])
29
- i = j + 1
30
- else:
31
- result.append(ch)
32
- i += 1
33
- text = ''.join(result)
34
- # Now apply the substitution only to non-quoted segments
35
- parts: list[str] = []
36
- i = 0
37
- n = len(text)
38
- while i < n:
39
- ch = text[i]
40
- if ch in ('"', "'"):
41
- quote = ch
42
- j = i + 1
43
- while j < n and text[j] != quote:
44
- j += 1
45
- parts.append(text[i:j + 1])
30
+ parts.append(expr_str[i:j + 1])
46
31
  i = j + 1
47
32
  else:
48
- # Accumulate non-quoted text
49
33
  j = i
50
- while j < n and text[j] not in ('"', "'"):
34
+ while j < n and expr_str[j] not in ('"', "'"):
51
35
  j += 1
52
- segment = text[i:j]
36
+ segment = expr_str[i:j]
53
37
  parts.append(re.sub(r'(\w+)\$', r'\1_S_', segment))
54
38
  i = j
55
39
  return ''.join(parts)
@@ -24,7 +24,7 @@ class LOCCCommandsMixin:
24
24
  """
25
25
  return getattr(self, '_noise_depol_p', 0.0)
26
26
 
27
- def cmd_locc(self, rest):
27
+ def cmd_locc(self, rest: str) -> None:
28
28
  args = rest.upper().split()
29
29
  if not args:
30
30
  if self.locc_mode:
@@ -86,7 +86,11 @@ class LOCCCommandsMixin:
86
86
  self.io.writeln(f"?MAX {LOCC_MAX_REGISTERS} registers (A-Z)")
87
87
  return
88
88
 
89
- sizes = [int(n) for n in nums]
89
+ try:
90
+ sizes = [int(n) for n in nums]
91
+ except ValueError:
92
+ self.io.writeln("?LOCC register sizes must be integers")
93
+ return
90
94
  total = sum(sizes)
91
95
  if joint and total > LOCC_MAX_JOINT_QUBITS:
92
96
  self.io.writeln(f"?JOINT mode limited to {LOCC_MAX_JOINT_QUBITS} total qubits (requested {total})")
@@ -130,7 +134,7 @@ class LOCCCommandsMixin:
130
134
  self.io.writeln(f" WARNING: non-depolarizing noise model active but not supported "
131
135
  f"in LOCC numpy path. Only NOISE depolarizing propagates to LOCC.")
132
136
 
133
- def cmd_send(self, rest):
137
+ def cmd_send(self, rest: str) -> None:
134
138
  if not self.locc_mode:
135
139
  self.io.writeln("?SEND requires LOCC mode")
136
140
  return
@@ -153,7 +157,7 @@ class LOCCCommandsMixin:
153
157
  self.locc.classical[var] = outcome
154
158
  self.io.writeln(f" {reg}[{qubit}] -> {var} = {outcome}")
155
159
 
156
- def cmd_share(self, rest):
160
+ def cmd_share(self, rest: str) -> None:
157
161
  if not self.locc_mode:
158
162
  self.io.writeln("?SHARE requires LOCC mode")
159
163
  return
@@ -173,7 +177,7 @@ class LOCCCommandsMixin:
173
177
  except RuntimeError as e:
174
178
  self.io.writeln(f"?{e}")
175
179
 
176
- def cmd_loccinfo(self):
180
+ def cmd_loccinfo(self) -> None:
177
181
  """Show LOCC protocol metrics after a run."""
178
182
  if not self.locc_mode:
179
183
  self.io.writeln("?NOT IN LOCC MODE")
@@ -10,7 +10,7 @@ class LOCCDisplayMixin:
10
10
  self._print_bloch_single(), self.print_histogram(), self.io.
11
11
  """
12
12
 
13
- def _locc_state(self, rest=''):
13
+ def _locc_state(self, rest: str = '') -> None:
14
14
  reg = rest.strip().upper() if rest else ''
15
15
  if self.locc.joint:
16
16
  if not reg or reg in self.locc.names:
@@ -24,7 +24,7 @@ class LOCCDisplayMixin:
24
24
  self.io.writeln(f"\n Register {name} ({size} qubits):")
25
25
  self._print_statevector(self.locc.svs[name], size)
26
26
 
27
- def _locc_bloch(self, rest):
27
+ def _locc_bloch(self, rest: str) -> None:
28
28
  m = re.match(r'([A-Z])\s*(\d*)', rest.strip(), re.IGNORECASE) if rest.strip() else None
29
29
  if m and m.group(1):
30
30
  reg = m.group(1).upper()
@@ -49,7 +49,7 @@ class LOCCDisplayMixin:
49
49
  else:
50
50
  self.io.writeln(f"?USAGE: BLOCH <reg> [qubit] (registers: {', '.join(self.locc.names)})")
51
51
 
52
- def _locc_display_results(self, per_reg, counts_joint):
52
+ def _locc_display_results(self, per_reg: dict, counts_joint: dict) -> None:
53
53
  """Display per-register and joint histograms."""
54
54
  for name in self.locc.names:
55
55
  size = self.locc.get_size(name)
@@ -27,7 +27,7 @@ class LOCCExecutionMixin:
27
27
  self.last_counts, self.io.
28
28
  """
29
29
 
30
- def _locc_run(self):
30
+ def _locc_run(self) -> None:
31
31
  """Execute program in LOCC mode (N registers)."""
32
32
  sorted_lines = sorted(self.program.keys())
33
33
  if not sorted_lines:
@@ -44,7 +44,7 @@ class LOCCExecutionMixin:
44
44
  # Sync last_sv from LOCC engine so EXPECT/DENSITY/BLOCH work
45
45
  self.last_sv = self._active_sv
46
46
 
47
- def _locc_run_with_send(self, sorted_lines, has_measure):
47
+ def _locc_run_with_send(self, sorted_lines: list[int], has_measure: bool) -> None:
48
48
  """LOCC execution with SEND — prefix/suffix split optimization.
49
49
 
50
50
  Executes the deterministic prefix (before first SEND) once,
@@ -148,7 +148,7 @@ class LOCCExecutionMixin:
148
148
  self._locc_display_results(per_reg, counts_joint)
149
149
  self.last_counts = counts_joint
150
150
 
151
- def _locc_run_vectorized(self, sorted_lines, has_measure):
151
+ def _locc_run_vectorized(self, sorted_lines: list[int], has_measure: bool) -> None:
152
152
  """LOCC execution without SEND — single execution, vectorized sampling.
153
153
 
154
154
  When noise is active, re-executes per shot so that Monte Carlo noise
@@ -314,7 +314,7 @@ class LOCCExecutionMixin:
314
314
 
315
315
  raise ValueError(f"LOCC mode requires @A/@B prefix, SEND, SHARE, or IF: {stmt}")
316
316
 
317
- def _locc_try_special(self, reg, stmt, run_vars):
317
+ def _locc_try_special(self, reg: str, stmt: str, run_vars: dict) -> bool:
318
318
  """Handle MEAS/RESET/MEASURE_X/Y/Z/SYNDROME in LOCC mode."""
319
319
  from qubasic_core.parser import parse_stmt
320
320
  from qubasic_core.statements import MeasStmt, ResetStmt, MeasureBasisStmt, SyndromeStmt
@@ -373,7 +373,7 @@ class LOCCExecutionMixin:
373
373
 
374
374
  return False
375
375
 
376
- def _locc_apply_gate(self, reg, gate_stmt):
376
+ def _locc_apply_gate(self, reg: str, gate_stmt: str) -> None:
377
377
  """Parse and apply a gate to a LOCC register via numpy.
378
378
 
379
379
  Uses LOCCRegBackend for standard gates. CTRL and INV modifiers
@@ -4,8 +4,8 @@ from __future__ import annotations
4
4
 
5
5
  import re
6
6
 
7
- from qubasic_core.engine import (
8
- GATE_TABLE, GATE_ALIASES,
7
+ from qubasic_core.gates import GATE_TABLE, GATE_ALIASES
8
+ from qubasic_core.patterns import (
9
9
  RE_LET_ARRAY, RE_LET_VAR, RE_PRINT,
10
10
  RE_GOTO, RE_GOSUB, RE_FOR, RE_NEXT, RE_WHILE, RE_IF_THEN,
11
11
  RE_MEAS, RE_RESET, RE_SEND, RE_SHARE, RE_AT_REG_LINE, RE_AT_REG,
@@ -3,6 +3,8 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import math
6
+ import os
7
+ import sys
6
8
  import time
7
9
  from typing import Any
8
10
 
@@ -111,10 +113,14 @@ class ProfilerMixin:
111
113
  self.io.writeln(f"\nRunning {n} trials...")
112
114
  for trial in range(n):
113
115
  old_io = self.io
116
+ old_stdout = sys.stdout
114
117
  self.io = _NullIOPort()
118
+ sys.stdout = open(os.devnull, 'w')
115
119
  try:
116
120
  self.cmd_run()
117
121
  finally:
122
+ sys.stdout.close()
123
+ sys.stdout = old_stdout
118
124
  self.io = old_io
119
125
  if self.last_counts:
120
126
  if len(self._stats_runs) >= MAX_STATS_RUNS:
@@ -32,7 +32,6 @@ class Scope:
32
32
 
33
33
  def __setitem__(self, name: str, value: Any) -> None:
34
34
  self._runtime[name] = value
35
- self._persistent[name] = value
36
35
 
37
36
  def keys(self):
38
37
  return set(self._persistent.keys()) | set(self._runtime.keys())
@@ -29,9 +29,12 @@ class SubroutineMixin:
29
29
  self._func_call_depth: int = 0
30
30
 
31
31
  def _scan_subs(self, sorted_lines: list[int]) -> None:
32
- """Scan program for SUB and FUNCTION blocks before execution."""
32
+ """Scan program for SUB/FUNCTION blocks and build jump table."""
33
33
  self._sub_defs.clear()
34
34
  self._func_defs.clear()
35
+ self._jump_table: dict[int, int] = {} # ip -> matching-end ip
36
+ # Build jump table for WHILE/WEND, DO/LOOP pairs
37
+ self._build_jump_table(sorted_lines)
35
38
  for i, ln in enumerate(sorted_lines):
36
39
  stmt = self.program[ln].strip()
37
40
  m = RE_SUB.match(stmt)
@@ -48,6 +51,27 @@ class SubroutineMixin:
48
51
  end_ip = self._find_end_block(sorted_lines, i, 'FUNCTION')
49
52
  self._func_defs[name] = {'params': params, 'start_ip': i + 1, 'end_ip': end_ip}
50
53
 
54
+ def _build_jump_table(self, sorted_lines: list[int]) -> None:
55
+ """Pre-compute WHILE->WEND and DO->LOOP ip mappings."""
56
+ while_stack: list[int] = []
57
+ do_stack: list[int] = []
58
+ for ip, ln in enumerate(sorted_lines):
59
+ s = self.program[ln].strip().upper()
60
+ if s.startswith('WHILE '):
61
+ while_stack.append(ip)
62
+ elif s == 'WEND':
63
+ if while_stack:
64
+ w_ip = while_stack.pop()
65
+ self._jump_table[w_ip] = ip
66
+ self._jump_table[ip] = w_ip
67
+ elif s.startswith('DO'):
68
+ do_stack.append(ip)
69
+ elif s.startswith('LOOP'):
70
+ if do_stack:
71
+ d_ip = do_stack.pop()
72
+ self._jump_table[d_ip] = ip
73
+ self._jump_table[ip] = d_ip
74
+
51
75
  def _find_end_block(self, sorted_lines: list[int], start_ip: int, kind: str) -> int:
52
76
  scan = start_ip + 1
53
77
  end_re = RE_END_SUB if kind == 'SUB' else RE_END_FUNCTION
@@ -423,7 +423,6 @@ class QBasicTerminal(Engine, ExecutorMixin, ExpressionMixin, DisplayMixin, DemoM
423
423
  # Program management
424
424
  'CHECKSUM': 'cmd_checksum',
425
425
  'VERSION': 'cmd_version',
426
- 'SEED': 'cmd_seed',
427
426
  'PROBE': 'cmd_probe',
428
427
  # Classic
429
428
  'RESTORE': 'cmd_restore',
@@ -461,6 +460,7 @@ class QBasicTerminal(Engine, ExecutorMixin, ExpressionMixin, DisplayMixin, DemoM
461
460
  # Circuit macros
462
461
  'CIRCUIT_DEF': 'cmd_circuit_def', 'APPLY_CIRCUIT': 'cmd_apply_circuit',
463
462
  'HELP': 'cmd_help', 'CONSISTENCY': 'cmd_consistency',
463
+ 'SEED': 'cmd_seed',
464
464
  }
465
465
 
466
466
  def dispatch(self, line: str) -> None:
@@ -568,7 +568,7 @@ class QBasicTerminal(Engine, ExecutorMixin, ExpressionMixin, DisplayMixin, DemoM
568
568
  gpu_ok = False
569
569
  try:
570
570
  _gb = AerSimulator(method='statevector', device='GPU')
571
- _gb.run(transpile(_pqc, _gb), shots=1).result()
571
+ _gb.run(transpile(_pqc_m, _gb), shots=1).result()
572
572
  gpu_ok = True
573
573
  except Exception:
574
574
  pass
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes