qubasic 0.6.0__tar.gz → 0.6.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.
- {qubasic-0.6.0 → qubasic-0.6.1}/CHANGELOG.md +9 -1
- {qubasic-0.6.0/qubasic.egg-info → qubasic-0.6.1}/PKG-INFO +12 -8
- {qubasic-0.6.0 → qubasic-0.6.1}/README.md +11 -7
- {qubasic-0.6.0 → qubasic-0.6.1}/pyproject.toml +16 -7
- {qubasic-0.6.0 → qubasic-0.6.1/qubasic.egg-info}/PKG-INFO +12 -8
- {qubasic-0.6.0 → qubasic-0.6.1}/qubasic.egg-info/SOURCES.txt +4 -2
- qubasic-0.6.1/qubasic.egg-info/entry_points.txt +2 -0
- {qubasic-0.6.0 → qubasic-0.6.1}/qubasic.egg-info/top_level.txt +0 -1
- {qubasic-0.6.0 → qubasic-0.6.1}/qubasic_core/__init__.py +1 -1
- qubasic-0.6.1/qubasic_core/__main__.py +6 -0
- {qubasic-0.6.0 → qubasic-0.6.1}/qubasic_core/analysis.py +1 -1
- qubasic-0.6.0/qubasic.py → qubasic-0.6.1/qubasic_core/cli.py +4 -3
- {qubasic-0.6.0 → qubasic-0.6.1}/qubasic_core/engine.py +2 -1
- {qubasic-0.6.0 → qubasic-0.6.1}/qubasic_core/engine_state.py +1 -0
- {qubasic-0.6.0 → qubasic-0.6.1}/qubasic_core/exec_context.py +1 -1
- {qubasic-0.6.0 → qubasic-0.6.1}/qubasic_core/expression.py +1 -1
- {qubasic-0.6.0 → qubasic-0.6.1}/qubasic_core/file_io.py +2 -1
- {qubasic-0.6.0 → qubasic-0.6.1}/qubasic_core/gates.py +2 -2
- {qubasic-0.6.0 → qubasic-0.6.1}/qubasic_core/locc_engine.py +3 -3
- {qubasic-0.6.0 → qubasic-0.6.1}/qubasic_core/locc_execution.py +5 -5
- {qubasic-0.6.0 → qubasic-0.6.1}/qubasic_core/memory.py +0 -4
- {qubasic-0.6.0 → qubasic-0.6.1}/qubasic_core/qol.py +7 -15
- {qubasic-0.6.0 → qubasic-0.6.1}/qubasic_core/terminal.py +15 -6
- qubasic-0.6.1/tests/test_features.py +2654 -0
- qubasic-0.6.1/tests/test_qubasic.py +1438 -0
- qubasic-0.6.0/qubasic.egg-info/entry_points.txt +0 -2
- qubasic-0.6.0/qubasic_core/__main__.py +0 -6
- {qubasic-0.6.0 → qubasic-0.6.1}/LICENSE +0 -0
- {qubasic-0.6.0 → qubasic-0.6.1}/MANIFEST.in +0 -0
- {qubasic-0.6.0 → qubasic-0.6.1}/examples/bell.qb +0 -0
- {qubasic-0.6.0 → qubasic-0.6.1}/examples/grover3.qb +0 -0
- {qubasic-0.6.0 → qubasic-0.6.1}/examples/locc_teleport.qb +0 -0
- {qubasic-0.6.0 → qubasic-0.6.1}/examples/sweep_rx.qb +0 -0
- {qubasic-0.6.0 → qubasic-0.6.1}/qubasic.egg-info/dependency_links.txt +0 -0
- {qubasic-0.6.0 → qubasic-0.6.1}/qubasic.egg-info/requires.txt +0 -0
- {qubasic-0.6.0 → qubasic-0.6.1}/qubasic_core/backend.py +0 -0
- {qubasic-0.6.0 → qubasic-0.6.1}/qubasic_core/classic.py +0 -0
- {qubasic-0.6.0 → qubasic-0.6.1}/qubasic_core/control_flow.py +0 -0
- {qubasic-0.6.0 → qubasic-0.6.1}/qubasic_core/debug.py +0 -0
- {qubasic-0.6.0 → qubasic-0.6.1}/qubasic_core/demos.py +0 -0
- {qubasic-0.6.0 → qubasic-0.6.1}/qubasic_core/display.py +0 -0
- {qubasic-0.6.0 → qubasic-0.6.1}/qubasic_core/errors.py +0 -0
- {qubasic-0.6.0 → qubasic-0.6.1}/qubasic_core/executor.py +0 -0
- {qubasic-0.6.0 → qubasic-0.6.1}/qubasic_core/help_text.py +0 -0
- {qubasic-0.6.0 → qubasic-0.6.1}/qubasic_core/io_protocol.py +0 -0
- {qubasic-0.6.0 → qubasic-0.6.1}/qubasic_core/locc.py +0 -0
- {qubasic-0.6.0 → qubasic-0.6.1}/qubasic_core/locc_commands.py +0 -0
- {qubasic-0.6.0 → qubasic-0.6.1}/qubasic_core/locc_display.py +0 -0
- {qubasic-0.6.0 → qubasic-0.6.1}/qubasic_core/mock_backend.py +0 -0
- {qubasic-0.6.0 → qubasic-0.6.1}/qubasic_core/noise_mixin.py +0 -0
- {qubasic-0.6.0 → qubasic-0.6.1}/qubasic_core/parser.py +0 -0
- {qubasic-0.6.0 → qubasic-0.6.1}/qubasic_core/patterns.py +0 -0
- {qubasic-0.6.0 → qubasic-0.6.1}/qubasic_core/profiler.py +0 -0
- {qubasic-0.6.0 → qubasic-0.6.1}/qubasic_core/program_mgmt.py +0 -0
- {qubasic-0.6.0 → qubasic-0.6.1}/qubasic_core/protocol.py +0 -0
- {qubasic-0.6.0 → qubasic-0.6.1}/qubasic_core/scope.py +0 -0
- {qubasic-0.6.0 → qubasic-0.6.1}/qubasic_core/screen.py +0 -0
- {qubasic-0.6.0 → qubasic-0.6.1}/qubasic_core/state_display.py +0 -0
- {qubasic-0.6.0 → qubasic-0.6.1}/qubasic_core/statements.py +0 -0
- {qubasic-0.6.0 → qubasic-0.6.1}/qubasic_core/strings.py +0 -0
- {qubasic-0.6.0 → qubasic-0.6.1}/qubasic_core/subs.py +0 -0
- {qubasic-0.6.0 → qubasic-0.6.1}/qubasic_core/sweep.py +0 -0
- {qubasic-0.6.0 → qubasic-0.6.1}/setup.cfg +0 -0
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.6.1 (2026-06-18)
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- Changing noise strength between runs (for example `NOISE amplitude_damping 0.1` then `0.9`) now rebuilds the circuit instead of reusing the first noise level, so sequential noise comparisons are correct.
|
|
7
|
+
|
|
8
|
+
### Changed
|
|
9
|
+
- The CLI now lives in `qubasic_core/cli.py`; the `qubasic` console script and `python -m qubasic_core` are unchanged, but the former top-level `qubasic` module is removed. Test suites moved under `tests/`.
|
|
10
|
+
|
|
3
11
|
## 0.6.0 (2026-06-18)
|
|
4
12
|
|
|
5
|
-
Correctness, scalability, and usability
|
|
13
|
+
Correctness, scalability, and usability improvements.
|
|
6
14
|
|
|
7
15
|
### Fixed
|
|
8
16
|
- **Time-travel debugging now works**: STEP records statevector checkpoints, so REWIND/FORWARD/HISTORY operate (previously `_checkpoint_sv` was never called).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: qubasic
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.1
|
|
4
4
|
Summary: Quantum BASIC Interactive Terminal
|
|
5
5
|
Author-email: "Charles C. Norton" <machineelv@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -73,11 +73,12 @@ Requires Python >= 3.10, Qiskit >= 1.0, qiskit-aer >= 0.13.
|
|
|
73
73
|
## Usage
|
|
74
74
|
|
|
75
75
|
```
|
|
76
|
-
|
|
77
|
-
python
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
76
|
+
qubasic Interactive REPL (installed console script)
|
|
77
|
+
python -m qubasic_core Same, without installing
|
|
78
|
+
qubasic script.qb Run a script file
|
|
79
|
+
qubasic --quiet script Suppress banner, output results only
|
|
80
|
+
qubasic --json script Machine-readable JSON output
|
|
81
|
+
qubasic --help Show CLI help
|
|
81
82
|
```
|
|
82
83
|
|
|
83
84
|
Headless (no REPL):
|
|
@@ -685,7 +686,7 @@ Programs with SEND use prefix/suffix splitting: the deterministic prefix (before
|
|
|
685
686
|
## JSON output
|
|
686
687
|
|
|
687
688
|
```
|
|
688
|
-
|
|
689
|
+
qubasic --json examples/bell.qb
|
|
689
690
|
```
|
|
690
691
|
|
|
691
692
|
```json
|
|
@@ -699,8 +700,9 @@ python qubasic.py --json examples/bell.qb
|
|
|
699
700
|
## Architecture
|
|
700
701
|
|
|
701
702
|
```
|
|
702
|
-
qubasic.py CLI entry point
|
|
703
703
|
qubasic_core/
|
|
704
|
+
cli.py CLI entry point (qubasic / python -m qubasic_core)
|
|
705
|
+
__main__.py Module entry point
|
|
704
706
|
engine_state.py Engine: standalone state container
|
|
705
707
|
terminal.py QBasicTerminal: REPL + command dispatch
|
|
706
708
|
engine.py Constants, gate tables, numpy simulation, LOCCEngine
|
|
@@ -729,6 +731,8 @@ qubasic_core/
|
|
|
729
731
|
demos.py Built-in demo circuits
|
|
730
732
|
protocol.py TerminalProtocol (mixin contract)
|
|
731
733
|
mock_backend.py MockAerSimulator for fast testing
|
|
734
|
+
tests/ Test suites (test_qubasic.py, test_features.py)
|
|
735
|
+
examples/ Sample .qb programs
|
|
732
736
|
```
|
|
733
737
|
|
|
734
738
|
`Engine` holds all program state. `QBasicTerminal` inherits `Engine` + 20 mixins. Execution methods live on `QBasicTerminal`, so headless/agent use should instantiate `QBasicTerminal` (the `Engine` base is a state container only).
|
|
@@ -40,11 +40,12 @@ Requires Python >= 3.10, Qiskit >= 1.0, qiskit-aer >= 0.13.
|
|
|
40
40
|
## Usage
|
|
41
41
|
|
|
42
42
|
```
|
|
43
|
-
|
|
44
|
-
python
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
43
|
+
qubasic Interactive REPL (installed console script)
|
|
44
|
+
python -m qubasic_core Same, without installing
|
|
45
|
+
qubasic script.qb Run a script file
|
|
46
|
+
qubasic --quiet script Suppress banner, output results only
|
|
47
|
+
qubasic --json script Machine-readable JSON output
|
|
48
|
+
qubasic --help Show CLI help
|
|
48
49
|
```
|
|
49
50
|
|
|
50
51
|
Headless (no REPL):
|
|
@@ -652,7 +653,7 @@ Programs with SEND use prefix/suffix splitting: the deterministic prefix (before
|
|
|
652
653
|
## JSON output
|
|
653
654
|
|
|
654
655
|
```
|
|
655
|
-
|
|
656
|
+
qubasic --json examples/bell.qb
|
|
656
657
|
```
|
|
657
658
|
|
|
658
659
|
```json
|
|
@@ -666,8 +667,9 @@ python qubasic.py --json examples/bell.qb
|
|
|
666
667
|
## Architecture
|
|
667
668
|
|
|
668
669
|
```
|
|
669
|
-
qubasic.py CLI entry point
|
|
670
670
|
qubasic_core/
|
|
671
|
+
cli.py CLI entry point (qubasic / python -m qubasic_core)
|
|
672
|
+
__main__.py Module entry point
|
|
671
673
|
engine_state.py Engine: standalone state container
|
|
672
674
|
terminal.py QBasicTerminal: REPL + command dispatch
|
|
673
675
|
engine.py Constants, gate tables, numpy simulation, LOCCEngine
|
|
@@ -696,6 +698,8 @@ qubasic_core/
|
|
|
696
698
|
demos.py Built-in demo circuits
|
|
697
699
|
protocol.py TerminalProtocol (mixin contract)
|
|
698
700
|
mock_backend.py MockAerSimulator for fast testing
|
|
701
|
+
tests/ Test suites (test_qubasic.py, test_features.py)
|
|
702
|
+
examples/ Sample .qb programs
|
|
699
703
|
```
|
|
700
704
|
|
|
701
705
|
`Engine` holds all program state. `QBasicTerminal` inherits `Engine` + 20 mixins. Execution methods live on `QBasicTerminal`, so headless/agent use should instantiate `QBasicTerminal` (the `Engine` base is a state container only).
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "qubasic"
|
|
7
|
-
version = "0.6.
|
|
7
|
+
version = "0.6.1"
|
|
8
8
|
description = "Quantum BASIC Interactive Terminal"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -42,10 +42,9 @@ charts = ["plotille>=5.0"]
|
|
|
42
42
|
readline = ["pyreadline3>=3.4; sys_platform == 'win32'"]
|
|
43
43
|
|
|
44
44
|
[project.scripts]
|
|
45
|
-
qubasic = "
|
|
45
|
+
qubasic = "qubasic_core.cli:main"
|
|
46
46
|
|
|
47
47
|
[tool.setuptools]
|
|
48
|
-
py-modules = ["qubasic"]
|
|
49
48
|
packages = ["qubasic_core"]
|
|
50
49
|
|
|
51
50
|
[tool.setuptools.package-data]
|
|
@@ -66,9 +65,19 @@ skip_empty = true
|
|
|
66
65
|
|
|
67
66
|
[tool.mypy]
|
|
68
67
|
python_version = "3.10"
|
|
69
|
-
|
|
68
|
+
ignore_missing_imports = true
|
|
70
69
|
warn_unused_configs = true
|
|
71
|
-
disallow_untyped_defs = true
|
|
72
|
-
check_untyped_defs = true
|
|
73
70
|
warn_redundant_casts = true
|
|
74
|
-
|
|
71
|
+
# QBasicTerminal composes 20 mixins; each mixin accesses attributes the host
|
|
72
|
+
# class provides, which mypy cannot resolve in isolation. The codebase is
|
|
73
|
+
# intentionally gradually typed, so the strict per-method settings and the
|
|
74
|
+
# cross-mixin attribute check are disabled while keeping mypy useful for real
|
|
75
|
+
# type mismatches, undefined names, and signature checks.
|
|
76
|
+
disallow_untyped_defs = false
|
|
77
|
+
check_untyped_defs = false
|
|
78
|
+
warn_return_any = false
|
|
79
|
+
warn_unused_ignores = false
|
|
80
|
+
# attr-defined: cross-mixin attributes the host class provides.
|
|
81
|
+
# has-type: types behind the lazy __getattr__ re-exports cannot be inferred.
|
|
82
|
+
# misc/union-attr: mixin base-class attribute overlap and optional access.
|
|
83
|
+
disable_error_code = ["attr-defined", "has-type", "misc", "union-attr"]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: qubasic
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.1
|
|
4
4
|
Summary: Quantum BASIC Interactive Terminal
|
|
5
5
|
Author-email: "Charles C. Norton" <machineelv@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -73,11 +73,12 @@ Requires Python >= 3.10, Qiskit >= 1.0, qiskit-aer >= 0.13.
|
|
|
73
73
|
## Usage
|
|
74
74
|
|
|
75
75
|
```
|
|
76
|
-
|
|
77
|
-
python
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
76
|
+
qubasic Interactive REPL (installed console script)
|
|
77
|
+
python -m qubasic_core Same, without installing
|
|
78
|
+
qubasic script.qb Run a script file
|
|
79
|
+
qubasic --quiet script Suppress banner, output results only
|
|
80
|
+
qubasic --json script Machine-readable JSON output
|
|
81
|
+
qubasic --help Show CLI help
|
|
81
82
|
```
|
|
82
83
|
|
|
83
84
|
Headless (no REPL):
|
|
@@ -685,7 +686,7 @@ Programs with SEND use prefix/suffix splitting: the deterministic prefix (before
|
|
|
685
686
|
## JSON output
|
|
686
687
|
|
|
687
688
|
```
|
|
688
|
-
|
|
689
|
+
qubasic --json examples/bell.qb
|
|
689
690
|
```
|
|
690
691
|
|
|
691
692
|
```json
|
|
@@ -699,8 +700,9 @@ python qubasic.py --json examples/bell.qb
|
|
|
699
700
|
## Architecture
|
|
700
701
|
|
|
701
702
|
```
|
|
702
|
-
qubasic.py CLI entry point
|
|
703
703
|
qubasic_core/
|
|
704
|
+
cli.py CLI entry point (qubasic / python -m qubasic_core)
|
|
705
|
+
__main__.py Module entry point
|
|
704
706
|
engine_state.py Engine: standalone state container
|
|
705
707
|
terminal.py QBasicTerminal: REPL + command dispatch
|
|
706
708
|
engine.py Constants, gate tables, numpy simulation, LOCCEngine
|
|
@@ -729,6 +731,8 @@ qubasic_core/
|
|
|
729
731
|
demos.py Built-in demo circuits
|
|
730
732
|
protocol.py TerminalProtocol (mixin contract)
|
|
731
733
|
mock_backend.py MockAerSimulator for fast testing
|
|
734
|
+
tests/ Test suites (test_qubasic.py, test_features.py)
|
|
735
|
+
examples/ Sample .qb programs
|
|
732
736
|
```
|
|
733
737
|
|
|
734
738
|
`Engine` holds all program state. `QBasicTerminal` inherits `Engine` + 20 mixins. Execution methods live on `QBasicTerminal`, so headless/agent use should instantiate `QBasicTerminal` (the `Engine` base is a state container only).
|
|
@@ -3,7 +3,6 @@ LICENSE
|
|
|
3
3
|
MANIFEST.in
|
|
4
4
|
README.md
|
|
5
5
|
pyproject.toml
|
|
6
|
-
qubasic.py
|
|
7
6
|
examples/bell.qb
|
|
8
7
|
examples/grover3.qb
|
|
9
8
|
examples/locc_teleport.qb
|
|
@@ -19,6 +18,7 @@ qubasic_core/__main__.py
|
|
|
19
18
|
qubasic_core/analysis.py
|
|
20
19
|
qubasic_core/backend.py
|
|
21
20
|
qubasic_core/classic.py
|
|
21
|
+
qubasic_core/cli.py
|
|
22
22
|
qubasic_core/control_flow.py
|
|
23
23
|
qubasic_core/debug.py
|
|
24
24
|
qubasic_core/demos.py
|
|
@@ -54,4 +54,6 @@ qubasic_core/statements.py
|
|
|
54
54
|
qubasic_core/strings.py
|
|
55
55
|
qubasic_core/subs.py
|
|
56
56
|
qubasic_core/sweep.py
|
|
57
|
-
qubasic_core/terminal.py
|
|
57
|
+
qubasic_core/terminal.py
|
|
58
|
+
tests/test_features.py
|
|
59
|
+
tests/test_qubasic.py
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
"""
|
|
3
|
-
QUBASIC —
|
|
3
|
+
QUBASIC — command-line entry point.
|
|
4
4
|
|
|
5
5
|
Usage:
|
|
6
|
-
|
|
7
|
-
python
|
|
6
|
+
qubasic Interactive REPL (installed console script)
|
|
7
|
+
python -m qubasic_core Same, run as a module
|
|
8
|
+
qubasic script.qb Run a script file
|
|
8
9
|
"""
|
|
9
10
|
|
|
10
11
|
import sys
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
from enum import Enum, auto
|
|
6
|
+
from typing import Any
|
|
6
7
|
import numpy as np
|
|
7
8
|
from qubasic_core.patterns import (
|
|
8
9
|
RE_LINE_NUM, RE_DEF_SINGLE, RE_DEF_BEGIN, RE_REG_INDEX,
|
|
@@ -79,7 +80,7 @@ try:
|
|
|
79
80
|
from rich.panel import Panel as _RichPanel
|
|
80
81
|
from rich.text import Text as _RichText
|
|
81
82
|
_RICH = True
|
|
82
|
-
_console = _RichConsole(highlight=False)
|
|
83
|
+
_console: Any = _RichConsole(highlight=False)
|
|
83
84
|
except ImportError:
|
|
84
85
|
_RICH = False
|
|
85
86
|
_console = None
|
|
@@ -71,6 +71,7 @@ class Engine:
|
|
|
71
71
|
self._circuit_cache_key: Any = None
|
|
72
72
|
self._circuit_cache: Any = None
|
|
73
73
|
self._pending_set_state: Any = None # immediate SET_STATE for next RUN
|
|
74
|
+
self._poke_state_prep: dict[Any, Any] = {} # qubit prep from POKE $0100
|
|
74
75
|
|
|
75
76
|
# LOCC
|
|
76
77
|
self.locc: Any = None
|
|
@@ -12,7 +12,7 @@ class ExecContext:
|
|
|
12
12
|
|
|
13
13
|
sorted_lines: list[int]
|
|
14
14
|
ip: int
|
|
15
|
-
run_vars: dict[str, Any]
|
|
15
|
+
run_vars: Any # dict[str, Any] or a Scope wrapper
|
|
16
16
|
loop_stack: list[dict[str, Any]] = field(default_factory=list)
|
|
17
17
|
iteration_count: int = 0
|
|
18
18
|
max_iterations: int = 100_000
|
|
@@ -116,7 +116,7 @@ class ExpressionMixin:
|
|
|
116
116
|
'SQRT2': math.sqrt(2), 'sqrt2': math.sqrt(2),
|
|
117
117
|
'True': True, 'False': False,
|
|
118
118
|
}
|
|
119
|
-
_AST_OPS = {
|
|
119
|
+
_AST_OPS: dict[type, Any] = {
|
|
120
120
|
ast.Add: operator.add, ast.Sub: operator.sub,
|
|
121
121
|
ast.Mult: operator.mul, ast.Div: operator.truediv,
|
|
122
122
|
ast.FloorDiv: operator.floordiv, ast.Mod: operator.mod,
|
|
@@ -6,6 +6,7 @@ import os
|
|
|
6
6
|
import re
|
|
7
7
|
import sys
|
|
8
8
|
from pathlib import Path
|
|
9
|
+
from typing import Any
|
|
9
10
|
|
|
10
11
|
import numpy as np
|
|
11
12
|
|
|
@@ -99,7 +100,7 @@ class FileIOMixin:
|
|
|
99
100
|
else:
|
|
100
101
|
f.write(f"LET {name} = {val}\n")
|
|
101
102
|
# Save program (exclude lines injected by IMPORT)
|
|
102
|
-
imported = getattr(self, '_imported_lines', set())
|
|
103
|
+
imported: set[int] = getattr(self, '_imported_lines', set())
|
|
103
104
|
saved = [n for n in sorted(self.program.keys()) if n not in imported]
|
|
104
105
|
for num in saved:
|
|
105
106
|
f.write(f"{num} {self.program[num]}\n")
|
|
@@ -242,8 +242,8 @@ def _measure_np(
|
|
|
242
242
|
"""
|
|
243
243
|
sv = np.ascontiguousarray(sv).reshape([2] * n_qubits)
|
|
244
244
|
ax = n_qubits - 1 - qubit
|
|
245
|
-
idx_0 = [slice(None)] * n_qubits
|
|
246
|
-
idx_1 = [slice(None)] * n_qubits
|
|
245
|
+
idx_0: list = [slice(None)] * n_qubits
|
|
246
|
+
idx_1: list = [slice(None)] * n_qubits
|
|
247
247
|
idx_0[ax] = 0
|
|
248
248
|
idx_1[ax] = 1
|
|
249
249
|
p0 = float(np.sum(np.abs(sv[tuple(idx_0)])**2))
|
|
@@ -42,7 +42,7 @@ class LOCCEngine:
|
|
|
42
42
|
self.joint = joint
|
|
43
43
|
self.noise_param = noise_param
|
|
44
44
|
self.noise_type = noise_type
|
|
45
|
-
self.classical = {}
|
|
45
|
+
self.classical: dict[str, int] = {}
|
|
46
46
|
# Precompute offsets for JOINT mode
|
|
47
47
|
self.offsets = []
|
|
48
48
|
off = 0
|
|
@@ -237,8 +237,8 @@ class LOCCEngine:
|
|
|
237
237
|
"""Sample and return (per_reg_counts, joint_counts)."""
|
|
238
238
|
if self.joint:
|
|
239
239
|
raw = _sample_np(self.sv, self.n_total, shots)
|
|
240
|
-
per_reg = {name: {} for name in self.names}
|
|
241
|
-
joint = {}
|
|
240
|
+
per_reg: dict[str, dict[str, int]] = {name: {} for name in self.names}
|
|
241
|
+
joint: dict[str, int] = {}
|
|
242
242
|
for state, count in raw.items():
|
|
243
243
|
# Split the joint bitstring into per-register segments.
|
|
244
244
|
# The joint statevector is ordered A[0..nA-1] B[0..nB-1] ...
|
|
@@ -110,8 +110,8 @@ class LOCCExecutionMixin:
|
|
|
110
110
|
snap = self.locc.snapshot()
|
|
111
111
|
snap_vars = dict(self.variables)
|
|
112
112
|
|
|
113
|
-
per_reg = {name: {} for name in self.locc.names}
|
|
114
|
-
counts_joint = {}
|
|
113
|
+
per_reg: dict[str, dict[str, int]] = {name: {} for name in self.locc.names}
|
|
114
|
+
counts_joint: dict[str, int] = {}
|
|
115
115
|
t0 = time.time()
|
|
116
116
|
progress_interval = max(1, shots // 10)
|
|
117
117
|
|
|
@@ -175,8 +175,8 @@ class LOCCExecutionMixin:
|
|
|
175
175
|
|
|
176
176
|
if noisy and has_measure:
|
|
177
177
|
# Per-shot execution so noise fires independently each time
|
|
178
|
-
per_reg = {name: {} for name in self.locc.names}
|
|
179
|
-
counts_joint = {}
|
|
178
|
+
per_reg: dict[str, dict[str, int]] = {name: {} for name in self.locc.names}
|
|
179
|
+
counts_joint: dict[str, int] = {}
|
|
180
180
|
for _shot in range(self.shots):
|
|
181
181
|
self.locc.reset()
|
|
182
182
|
try:
|
|
@@ -433,7 +433,7 @@ class LOCCExecutionMixin:
|
|
|
433
433
|
if info is None:
|
|
434
434
|
raise ValueError(f"UNKNOWN GATE: {inner_name}")
|
|
435
435
|
n_params, n_qubits_needed = info
|
|
436
|
-
params =
|
|
436
|
+
params = tuple(self.eval_expr(a) for a in inv_args[:n_params])
|
|
437
437
|
qubits = [self._resolve_qubit(a, n_qubits=n_reg) for a in inv_args[n_params:n_params+n_qubits_needed]]
|
|
438
438
|
matrix = _np_gate_matrix(inner_name, tuple(params)).conj().T
|
|
439
439
|
self.locc.apply_matrix(reg, matrix, qubits)
|
|
@@ -197,14 +197,10 @@ class MemoryMixin:
|
|
|
197
197
|
p1 = max(0.0, min(1.0, v))
|
|
198
198
|
theta = 2 * math.asin(math.sqrt(p1))
|
|
199
199
|
# This requires a circuit context — store for next RUN
|
|
200
|
-
if not hasattr(self, '_poke_state_prep'):
|
|
201
|
-
self._poke_state_prep = {}
|
|
202
200
|
self._poke_state_prep[qubit] = ('RY', theta)
|
|
203
201
|
self.io.writeln(f"POKE q{qubit}: P(1)={p1:.4f} -> RY({theta:.4f})")
|
|
204
202
|
elif field in (1, 2, 3):
|
|
205
203
|
# Bloch x, y, z — store target vector
|
|
206
|
-
if not hasattr(self, '_poke_state_prep'):
|
|
207
|
-
self._poke_state_prep = {}
|
|
208
204
|
labels = {1: 'Bx', 2: 'By', 3: 'Bz'}
|
|
209
205
|
self._poke_state_prep[(qubit, labels[field])] = v
|
|
210
206
|
self.io.writeln(f"POKE q{qubit}.{labels[field]} = {v:.4f}")
|
|
@@ -13,6 +13,13 @@ from typing import Any
|
|
|
13
13
|
import numpy as np
|
|
14
14
|
|
|
15
15
|
|
|
16
|
+
class _NullIO:
|
|
17
|
+
"""IOPort that swallows output, used to silence inner runs (COMPARE/PLOT/ANIMATE)."""
|
|
18
|
+
def write(self, text: str) -> None: pass
|
|
19
|
+
def writeln(self, text: str) -> None: pass
|
|
20
|
+
def read_line(self, prompt: str) -> str: return ''
|
|
21
|
+
|
|
22
|
+
|
|
16
23
|
# ═══════════════════════════════════════════════════════════════════════
|
|
17
24
|
# Quantum spinner (#21)
|
|
18
25
|
# ═══════════════════════════════════════════════════════════════════════
|
|
@@ -295,11 +302,6 @@ class QoLMixin:
|
|
|
295
302
|
for m in [m1, m2]:
|
|
296
303
|
self.sim_method = m
|
|
297
304
|
old_io = self.io
|
|
298
|
-
|
|
299
|
-
class _NullIO:
|
|
300
|
-
def write(self, t: str) -> None: pass
|
|
301
|
-
def writeln(self, t: str) -> None: pass
|
|
302
|
-
def read_line(self, p: str) -> str: return ''
|
|
303
305
|
self.io = _NullIO()
|
|
304
306
|
try:
|
|
305
307
|
self.cmd_run()
|
|
@@ -372,11 +374,6 @@ class QoLMixin:
|
|
|
372
374
|
delay = float(parts[4]) / 1000 if len(parts) > 4 else 0.3
|
|
373
375
|
values = [start + (end - start) * i / max(1, steps - 1) for i in range(steps)]
|
|
374
376
|
|
|
375
|
-
class _NullIO:
|
|
376
|
-
def write(self, t: str) -> None: pass
|
|
377
|
-
def writeln(self, t: str) -> None: pass
|
|
378
|
-
def read_line(self, p: str) -> str: return ''
|
|
379
|
-
|
|
380
377
|
for i, val in enumerate(values):
|
|
381
378
|
self.variables[var] = val
|
|
382
379
|
old_io = self.io
|
|
@@ -486,11 +483,6 @@ class QoLMixin:
|
|
|
486
483
|
steps = int(parts[3]) if len(parts) > 3 else 20
|
|
487
484
|
values = [start + (end - start) * i / max(1, steps - 1) for i in range(steps)]
|
|
488
485
|
|
|
489
|
-
class _NullIO:
|
|
490
|
-
def write(self, t: str) -> None: pass
|
|
491
|
-
def writeln(self, t: str) -> None: pass
|
|
492
|
-
def read_line(self, p: str) -> str: return ''
|
|
493
|
-
|
|
494
486
|
xs, ys = [], []
|
|
495
487
|
for val in values:
|
|
496
488
|
self.variables[var] = val
|
|
@@ -6,6 +6,7 @@ import re
|
|
|
6
6
|
import time
|
|
7
7
|
import warnings as _warnings
|
|
8
8
|
from collections import OrderedDict
|
|
9
|
+
from typing import Any
|
|
9
10
|
|
|
10
11
|
# Quiet the third-party dependency-version notice (urllib3/chardet) that
|
|
11
12
|
# qiskit's transitive imports emit, so it doesn't precede the banner.
|
|
@@ -681,13 +682,13 @@ class QBasicTerminal(Engine, ExecutorMixin, ExpressionMixin, DisplayMixin, DemoM
|
|
|
681
682
|
self.io.writeln("?USAGE: DELETE <line> or DELETE <start>-<end>")
|
|
682
683
|
return
|
|
683
684
|
if '-' in rest:
|
|
684
|
-
|
|
685
|
-
|
|
685
|
+
lo_s, hi_s = rest.split('-')
|
|
686
|
+
lo, hi = int(lo_s.strip()), int(hi_s.strip())
|
|
686
687
|
for k in list(self.program.keys()):
|
|
687
|
-
if
|
|
688
|
+
if lo <= k <= hi:
|
|
688
689
|
del self.program[k]
|
|
689
690
|
self._parsed.pop(k, None)
|
|
690
|
-
self.io.writeln(f"DELETED {
|
|
691
|
+
self.io.writeln(f"DELETED {lo}-{hi}")
|
|
691
692
|
else:
|
|
692
693
|
n = int(rest)
|
|
693
694
|
if n in self.program:
|
|
@@ -857,6 +858,7 @@ class QBasicTerminal(Engine, ExecutorMixin, ExpressionMixin, DisplayMixin, DemoM
|
|
|
857
858
|
def _accumulate_type_field(self, line: str) -> None:
|
|
858
859
|
"""Accumulate a field for a pending TYPE definition, or finalize on END TYPE."""
|
|
859
860
|
from qubasic_core.engine import RE_TYPE_FIELD, RE_END_TYPE
|
|
861
|
+
assert self._pending_type is not None # only called while a TYPE is open
|
|
860
862
|
stripped = line.strip()
|
|
861
863
|
if RE_END_TYPE.match(stripped):
|
|
862
864
|
# Finalize the type definition
|
|
@@ -1081,7 +1083,14 @@ class QBasicTerminal(Engine, ExecutorMixin, ExpressionMixin, DisplayMixin, DemoM
|
|
|
1081
1083
|
|
|
1082
1084
|
def _run_with_fallback(self, qc, backend_opts: dict, method: str) -> 'Any':
|
|
1083
1085
|
"""Run the circuit with shots, handling GPU and stabilizer fallbacks."""
|
|
1084
|
-
|
|
1086
|
+
# str(NoiseModel) does not vary with channel parameters (amplitude_damping
|
|
1087
|
+
# 0.1 and 0.9 stringify identically), so fingerprint noise by the exact
|
|
1088
|
+
# NOISE command and the per-qubit memory-mapped noise instead — otherwise
|
|
1089
|
+
# changing noise strength between runs reuses the first circuit.
|
|
1090
|
+
_noise_key = (
|
|
1091
|
+
getattr(self, '_noise_spec', None),
|
|
1092
|
+
tuple(sorted(getattr(self, '_qubit_noise', {}).items())),
|
|
1093
|
+
)
|
|
1085
1094
|
# Numeric variable bindings affect gate parameters baked into the
|
|
1086
1095
|
# transpiled circuit, so they must be part of the cache key. Without
|
|
1087
1096
|
# this, parameter sweeps that re-run via cmd_run (PLOT, ANIMATE) reuse
|
|
@@ -2173,7 +2182,7 @@ class QBasicTerminal(Engine, ExecutorMixin, ExpressionMixin, DisplayMixin, DemoM
|
|
|
2173
2182
|
if self.last_circuit is None:
|
|
2174
2183
|
self.io.writeln("?NO CIRCUIT — RUN first")
|
|
2175
2184
|
return
|
|
2176
|
-
ops = {}
|
|
2185
|
+
ops: dict[str, int] = {}
|
|
2177
2186
|
for inst in self.last_circuit.data:
|
|
2178
2187
|
name = inst.operation.name
|
|
2179
2188
|
ops[name] = ops.get(name, 0) + 1
|